繁体   English   中英

在 Java 8 中使用 groupBy() 按两个属性对对象进行分组

[英]Group objects by two properties in using groupBy() in Java 8

我有一个人 class 如下所示:

public class Person {
    private int id;
    private String name;
    private int score;

    // constructor, getters, etc.
}

我有一个Persone对象列表,我想将它们分组到Map中。

主class:

public class Main {
    public static void main(String[] args) {
        Person person1 = new Person(1, "John", 4);
        Person person2 = new Person(2, "John", 3);
        Person person3 = new Person(2, "John", 4);

        List<Person> personList = new ArrayList<>();
        personList.add(person1);
        personList.add(person2);
        personList.add(person3);

        Map<String, List<Person>> personByName = personList.stream()
                .collect(groupingBy(Person::getName));

        System.out.println(personByName);
    }

}

它给出了结果:

{John=[Person{id=1, name='John', score=4},
       Person{id=2, name='John', score=3},
       Person{id=2, name='John', score=4}]}

如何按name分组,但仍然通过将两个人分别保存在id中来区分两个人?

我希望结果如下所示:

{John=[Person{id=1, name='John', score=4}],
 John=[Person{id=2, name='John', score=3}, Person{id=2, name='John', score=4}]}

groupingBy()List转换为Map<String,List>key = John,Value = List。

toMap()List转换为Map<String,Person> , Key = John, value = Person, Map中的key不能重复,所以必须改名字。

Person person1 = new Person(1, "John1", 4);
Person person2 = new Person(2, "John2", 3);
Person person3 = new Person(2, "John3", 4);

List<Person> personList = new ArrayList<>();
personList.add(person1);
personList.add(person2);
personList.add(person3);


Map<String, Person> personByName = personList.stream().collect(Collectors.toMap(Person::getName,Function.identity()));

System.out.println(personByName);

Output:

{John1=Person(id=1, name=John1, score=4), John2=Person(id=2, name=John2, score=3), John3=Person(id=2, name=John3, score=4)}

如何按name分组,但仍然通过将两个人分别保存在id中来区分两个人?

Map中本质上不可能有两个相同的密钥

但是有一些选项可以以这种方式对数据进行分组。

注意: ID 通常应该是唯一的,但在您的示例中它不是,所以我会对此视而不见。 as the second property for grouping, I'll proceed with grouping person objects by ID尽管我们可能会使用作为分组的第二个属性,但我将继续按 ID 对 person 对象进行分组

要按name然后按id对人员进行分组,您可以创建一个嵌套的 map Map<String,Map<Integer,List<Person>>> ,它将每个唯一name与 map 相关联,允许检索具有特定id的(和显然是同一个名字)。

这就是它的实现方式:

 List<Person> personList = new ArrayList<>();
Collections.addAll(personList,               // with Java 9+ use List.of() instead
    new Person(1, "John", 4),
    new Person(2, "John", 3),
    new Person(2, "John", 4)
);

Map<String, Map<Integer, List<Person>>> personByNameAndId = personList.stream()
    .collect(Collectors.groupingBy(
        Person::getName,
        Collectors.groupingBy(Person::getId)
    ));
        
personByNameAndId.forEach((name, personById) -> {
    System.out.println(name);
    personById.forEach((id, people) -> people.forEach(p -> System.out.println("id " + id + " -> " + p)));
});

Output:

John
id 1 -> Person{id=1, name='John', score=4}
id 2 -> Person{id=2, name='John', score=3}
id 2 -> Person{id=2, name='John', score=4}

值得一提的是,建议避免使用嵌套的 collections,因为它们很麻烦,如果您在拨号时不够专心,可能会引入错误。 例如,使用映射的 map 时,您可能会在调用get()时意外交换的顺序,并且因为Map.get()需要一个类型为Object的参数,所以编译代码仍然会

另一种选择是引入一个 object 来保存一个人的nameid 它可以实现为class (这将是 Java 8 的唯一选项):

public class IdName {
    private final int id;
    private final String name;
    
    public IdName(Person p) {
        this(p.getId(), p.getName());
    }
    
    // getters, all-args-constructor, equals/hashcode
}

或作为 Java 16记录

public record IdName(int id, String name) {
    public IdName(Person p) {
        this(p.getId(), p.getName());
    }
}

为了便于将Person转换为IdName ,您可以在Person class 中引入需要Person实例的IdName构造函数(如上所示),或toIdName()的便捷方法。

如何使用此自定义 object 对人员进行分组,一个并从 map 检索与给定人员共享nameidList<Person>

Map<IdName, List<Person>> personByNameAndId = personList.stream()
    .collect(Collectors.groupingBy(IdName::new));

Person john = new Person(2, "John", 0);
personByNameAndId.get(new IdName(john))
    .forEach(System.out::println);

Output:

Person{id=2, name='John', score=3}
Person{id=2, name='John', score=4}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM