The title is awesome, but Collectors.groupingBy is just a terrific story.
Recently, there have been many cases of creating various maps using groupingBy (for some reason, there were few cases of creating maps, so I didn't use them much). This article is a summary of what was investigated at that time.
Java10 Lombok
The good thing about Collectors.groupingBy is that you can easily aggregate them by giving them a key. If you want to change the value of Map, you can add another argument and write it there, so it supports aggregation in various cases.
List<Integer> nums = List.of(1,2,3,4,5,6,7,8,9,1,1,1);
Map<Integer,List<Integer>> map = nums.stream().collect(Collectors.groupingBy(i ->i));
System.out.println(map);
=> {1=[1, 1, 1, 1], 2=[2], 3=[3], 4=[4], 5=[5], 6=[6], 7=[7], 8=[8], 9=[9]}
However, the biggest feature of groupingBy is that it does well even if you set complicated keys. Setting multiple keys is very easy, just pass multiple fields when specifying the keys. "-" Is added for easy viewing, but it is okay without it.
As the key this time, name1 and name2 will be set as keys.
public static void main(String[] arg) {
List<String> names = List.of("taro", "jiro", "saburo");
List<Name> nameList = names.stream().map(it -> new Name(it, it, it)).collect(Collectors.toList());
Map<String,List<Name>> map = nameList.stream()
.collect(Collectors.groupingBy(it -> it.getName1() + "-" + it.getName2()));
System.out.println(map);
// => {jiro-jiro=[Main.Name(name1=jiro, name2=jiro, name3=jiro)], saburo-saburo=[Main.Name(name1=saburo, name2=saburo, name3=saburo)], taro-taro=[Main.Name(name1=taro, name2=taro, name3=taro)]}
}
@Data
private static class Name {
private String name1;
private String name2;
private String name3;
public Name(String name1, String name2, String name3) {
this.name1 = name1;
this.name2 = name2;
this.name3 = name3;
}
}
With this alone, name3 has the same value, so you cannot tell whether name1 and name2 are the keys.
So I would like to add new Name ("taro", "jiro", "taro")
to the List and see what kind of map can be created.
public static void main(String[] arg) {
List<String> names = List.of("taro", "jiro", "saburo");
List<Name> nameList = names.stream().map(it -> new Name(it, it, it)).collect(Collectors.toList());
nameList.add(new Name("taro", "jiro", "taro"));
Map<String,List<Name>> map = nameList.stream()
.collect(Collectors.groupingBy(it -> it.getName1() + "-" + it.getName2()));
System.out.println(map);
// => {jiro-jiro=[Main.Name(name1=jiro, name2=jiro, name3=jiro)], saburo-saburo=[Main.Name(name1=saburo, name2=saburo, name3=saburo)], taro-taro=[Main.Name(name1=taro, name2=taro, name3=taro)], taro-jiro=[Main.Name(name1=taro, name2=jiro, name3=taro)]}
}
As you can see from the result, the Object added this time is independent as taro-jiro = [Main.Name (name1 = taro, name2 = jiro, name3 = taro)]
, so name1 and name2 are You can see that it was the key.
What is worrisome here is that the resulting name3 contains "taro". The great thing about groupingBy is that it does the same for the values of fields other than those specified in the key, even if they are the same as the other fields.
I would like to run with name1 and name3 as keys to verify if it is true.
public static void main(String[] arg) {
List<String> names = List.of("taro", "jiro", "saburo");
List<Name> nameList = names.stream().map(it -> new Name(it, it, it)).collect(Collectors.toList());
nameList.add(new Name("taro", "jiro", "taro"));
Map<String,List<Name>> map = nameList.stream()
.collect(Collectors.groupingBy(it -> it.getName1() + "-" + it.getName3()));
System.out.println(map);
// => {jiro-jiro=[Main.Name(name1=jiro, name2=jiro, name3=jiro)], saburo-saburo=[Main.Name(name1=saburo, name2=saburo, name3=saburo)], taro-taro=[Main.Name(name1=taro, name2=taro, name3=taro), Main.Name(name1=taro, name2=jiro, name3=taro)]}
}
When you check the execution result, the Object added as new Name ("taro", "jiro", "taro")
istaro-taro = [Main.Name (name1 = taro, name2 = taro, name3 = taro) ), Main.Name (name1 = taro, name2 = jiro, name3 = taro)]
and is registered as the value of the taro-taro
key as expected.
Let's look at another pattern of name1 and name3 examples. You can see the fields up to the previous example, but let's also look at the example where name1 and name3 are exchanged.
public static void main(String[] arg) {
List<String> names = List.of("taro", "jiro", "saburo");
List<Name> nameList = names.stream().map(it -> new Name(it, it, it)).collect(Collectors.toList());
nameList.add(new Name("taro", "jiro", "taro"));
nameList.add(new Name("jiro", "taro", "taro"));
Map<String,List<Name>> map = nameList.stream()
.collect(Collectors.groupingBy(it -> it.getName1() + "-" + it.getName3()));
System.out.println(map);
// => {jiro-jiro=[Main.Name(name1=jiro, name2=jiro, name3=jiro)], saburo-saburo=[Main.Name(name1=saburo, name2=saburo, name3=saburo)], jiro-taro=[Main.Name(name1=jiro, name2=taro, name3=taro)], taro-taro=[Main.Name(name1=taro, name2=taro, name3=taro), Main.Name(name1=taro, name2=jiro, name3=taro)]}
}
It seems that they will respond properly even if the order is changed.
I tried the previous example with Integer type.
public static void main(String[] arg) {
List<Integer> nums = List.of(1,2,3);
List<Name> nameList = nums.stream().map(it -> new Name(it, it, it)).collect(Collectors.toList());
nameList.add(new Name(1, 2, 1));
nameList.add(new Name(2, 1, 1));
Map<Integer,List<Name>> map = nameList.stream()
.collect(Collectors.groupingBy(it -> it.getName1() + it.getName2()));
System.out.println(map);
}
@Data
private static class Name {
private Integer name1;
private Integer name2;
private Integer name3;
public Name(Integer name1, Integer name2, Integer name3) {
this.name1 = name1;
this.name2 = name2;
this.name3 = name3;
}
}
In this case, it seems to be aggregated by the calculation result of the key. It would be strange if there were multiple keys with the same key, so I'm convinced.
It seems that it is aggregated by the calculation result, but just in case, check if you can see it in the field.
public static void main(String[] arg) {
List<Integer> nums = List.of(1,2,3);
List<Name> nameList = nums.stream().map(it -> new Name(it, it, it)).collect(Collectors.toList());
nameList.add(new Name(1, 1, 2));
nameList.add(new Name(1, 2, 1));
Map<Integer,List<Name>> map = nameList.stream()
.collect(Collectors.groupingBy(it -> it.getName1() + it.getName3()));
System.out.println(map);
// => 2=[Main.Name(name1=1, name2=1, name3=1), Main.Name(name1=1, name2=2, name3=1)], 3=[Main.Name(name1=1, name2=1, name3=2)], 4=[Main.Name(name1=2, name2=2, name3=2)], 6=[Main.Name(name1=3, name2=3, name3=3)]}
}
@Data
private static class Name {
private Integer name1;
private Integer name2;
private Integer name3;
public Name(Integer name1, Integer name2, Integer name3) {
this.name1 = name1;
this.name2 = name2;
this.name3 = name3;
}
}
He also saw it in Integer type.
This is the third trial of Integer type. Now suppose you convert the key to a string and use it as the key.
public static void main(String[] arg) {
List<Integer> nums = List.of(1,2,3);
List<Name> nameList = nums.stream().map(it -> new Name(it, it, it)).collect(Collectors.toList());
nameList.add(new Name(1, 2, 1));
nameList.add(new Name(2, 1, 1));
Map<String,List<Name>> map = nameList.stream()
.collect(Collectors.groupingBy(it -> it.getName1() + "-" + it.getName2()));
System.out.println(map);
// => {1-1=[Main.Name(name1=1, name2=1, name3=1)], 2-1=[Main.Name(name1=2, name2=1, name3=1)], 1-2=[Main.Name(name1=1, name2=2, name3=1)], 2-2=[Main.Name(name1=2, name2=2, name3=2)], 3-3=[Main.Name(name1=3, name2=3, name3=3)]}
}
@Data
private static class Name {
private Integer name1;
private Integer name2;
private Integer name3;
public Name(Integer name1, Integer name2, Integer name3) {
this.name1 = name1;
this.name2 = name2;
this.name3 = name3;
}
}
In this case, the order seems to be taken into consideration.
Let's check if there is no problem even if we increase the number of keys.
public static void main(String[] arg) {
List<String> names = List.of("taro", "jiro", "saburo");
List<Name> nameList = names.stream().map(it -> new Name(it, it, it)).collect(Collectors.toList());
nameList.add(new Name("taro","taro","jiro"));
nameList.add(new Name("taro","jiro","taro"));
Map<String,List<Name>> map = nameList.stream()
.collect(Collectors.groupingBy(it -> it.getName1() + "-" + it.getName2() + "-" + it.getName3()));
System.out.println(map);
// => {jiro-jiro-jiro=[Main.Name(name1=jiro, name2=jiro, name3=jiro)], taro-jiro-taro=[Main.Name(name1=taro, name2=jiro, name3=taro)], taro-taro-taro=[Main.Name(name1=taro, name2=taro, name3=taro)], taro-taro-jiro=[Main.Name(name1=taro, name2=taro, name3=jiro)], saburo-saburo-saburo=[Main.Name(name1=saburo, name2=saburo, name3=saburo)]}
}
@Data
private static class Name {
private String name1;
private String name2;
private String name3;
public Name(String name1, String name2, String name3) {
this.name1 = name1;
this.name2 = name2;
this.name3 = name3;
}
}
It seems that it is okay to increase the number of keys.
I thought I'd try a little more, but now that I've grasped the tendency, I'll finish by summarizing the operation of going up By.
Basically, it seems to be a method that creates the field specified in the key according to the Lambda expression and aggregates it by that value. If it is a String type, you can see that the specified order and the field are linked and the key is, and if it is a numeric type, you can see if the created result is the same because the calculation result of the specified field is the key. I will.
It turns out that goupingBy is really powerful. It can be even stronger when combined with other methods in Collectors, but I'd like to take another opportunity (I don't know if there is one).
By the way, I haven't looked at the internal processing of Java in this article, so I don't know why this happens!
Recommended Posts