在java 8中按多个字段名分组 [英] Group by multiple field names in java 8
问题描述
我找到了通过POJO的某个字段名称对对象进行分组的代码。下面是代码:
I found the code for grouping the objects by some field name from POJO. Below is the code for that:
public class Temp {
static class Person {
private String name;
private int age;
private long salary;
Person(String name, int age, long salary) {
this.name = name;
this.age = age;
this.salary = salary;
}
@Override
public String toString() {
return String.format("Person{name='%s', age=%d, salary=%d}", name, age, salary);
}
}
public static void main(String[] args) {
Stream<Person> people = Stream.of(new Person("Paul", 24, 20000),
new Person("Mark", 30, 30000),
new Person("Will", 28, 28000),
new Person("William", 28, 28000));
Map<Integer, List<Person>> peopleByAge;
peopleByAge = people
.collect(Collectors.groupingBy(p -> p.age, Collectors.mapping((Person p) -> p, toList())));
System.out.println(peopleByAge);
}
}
输出是(这是正确的):
And the output is (which is correct):
{24=[Person{name='Paul', age=24, salary=20000}], 28=[Person{name='Will', age=28, salary=28000}, Person{name='William', age=28, salary=28000}], 30=[Person{name='Mark', age=30, salary=30000}]}
但如果我想按多个字段分组怎么办?显然,在那个POJO中实现 equals()
方法之后,我可以在 groupingBy()
方法中传递一些POJO但是有没有其他选项,比如我可以通过给定POJO中的多个字段进行分组?
But what if I want to group by multiple fields? I can obviously pass some POJO in groupingBy()
method after implementing equals()
method in that POJO but is there any other option like I can group by more than one fields from the given POJO?
例如在我的情况下,我想按姓名和年龄分组。
E.g. here in my case, I want to group by name and age.
推荐答案
这里有几个选项。最简单的是链接你的收藏家:
You have a few options here. The simplest is to chain your collectors:
Map<String, Map<Integer, List<Person>>> map = people
.collect(Collectors.groupingBy(Person::getName,
Collectors.groupingBy(Person::getAge));
然后,要获得一份名为Fred的18岁人员名单,您将使用:
Then to get a list of 18 year old people called Fred you would use:
map.get("Fred").get(18);
第二个选项是定义一个表示分组的类。这可以在Person中:
A second option is to define a class that represents the grouping. This can be inside Person:
class Person {
public static class NameAge {
public NameAge(String name, int age) {
...
}
// must implement equals and hash function
}
public NameAge getNameAge() {
return new NameAge(name, age);
}
}
然后你可以使用:
Map<NameAge, List<Person>> map = people.collect(Collectors.groupingBy(Person::getNameAge));
和用
map.get(new NameAge("Fred", 18));
最后,如果你不想实现自己的组类,那么许多Java框架都有一个对
类,专为此类事物而设计。例如: apache commons pair 如果你使用这些库中的一个,然后你可以为地图的关键字创建一对名称和年龄:
Finally if you don't want to implement your own group class then many of the Java frameworks around have a pair
class designed for exactly this type of thing. For example: apache commons pair If you use one of these libraries then you can make the key to the map a pair of the name and age:
Map<Pair<String, Integer>, List<Person>> map =
people.collect(Collectors.groupingBy(p -> Pair.of(p.getName(), p.getAge())));
并检索:
map.get(Pair.of("Fred", 18));
就我个人而言,我真的不喜欢这些元组库。它们似乎与良好的OO设计完全相反:它们隐藏意图而不是暴露它。
Personally I really dislike these tuple libraries. They seem to be the exact opposite of good OO design: they hide intent instead of exposing it.
说过你可以通过定义自己的分组来组合后两个选项类,但通过扩展 Pair
来实现它 - 这为您节省了很多定义等于
等所涉及的工作并隐藏使用元组只是一个方便的实现细节,就像任何其他集合一样。
Having said that you can combine the second two options by defining your own grouping class but implementing it by just extending Pair
- that saves you a lot of the work involved in defining equals
etc and hides the use of the tuple as just a convenient implementation detail like any other collection.
祝你好运。
这篇关于在java 8中按多个字段名分组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!