首页 > 解决方案 > 使用java流按相同类型的2个键分组

问题描述

使用 java 流,如何从 List 创建一个 Map 以通过同一类上的 2 个键进行索引?

我在这里给出一个代码示例,我希望地图“personByName”通过名字或姓氏来获取所有人,所以我想获得 3 个“史蒂夫”:当它是他们的名字或姓氏时。我不知道如何混合 2 Collectors.groupingBy。

public static class Person {
    final String firstName;
    final String lastName;

    protected Person(String firstName, String lastName) {
        super();
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }

}

@Test
public void testStream() {
    List<Person> persons = Arrays.asList(
            new Person("Bill", "Gates"),
            new Person("Bill", "Steve"),
            new Person("Steve", "Jobs"),
            new Person("Steve", "Wozniac"));

    Map<String, Set<Person>> personByFirstName = persons.stream().collect(Collectors.groupingBy(Person::getFirstName, Collectors.toSet()));
    Map<String, Set<Person>> personByLastName = persons.stream().collect(Collectors.groupingBy(Person::getLastName, Collectors.toSet()));

    Map<String, Set<Person>> personByName = persons.stream().collect(Collectors.groupingBy(Person::getLastName, Collectors.toSet()));// This is wrong, I want bot first and last name

    Assert.assertEquals("we should search by firstName AND lastName", 3, personByName.get("Steve").size()); // This fails

}

我通过循环 2 个地图找到了一种解决方法,但它不是面向流的。

标签: javajava-8java-stream

解决方案


你可以这样做:

Map<String, Set<Person>> personByName = persons.stream()
       .flatMap(p -> Stream.of(new SimpleEntry<>(p.getFirstName(), p),
                               new SimpleEntry<>(p.getLastName(), p)))
       .collect(Collectors.groupingBy(SimpleEntry::getKey,
                   Collectors.mapping(SimpleEntry::getValue, Collectors.toSet())));

假设您toString()向该类添加一个方法Person,然后您可以使用以下命令查看结果:

List<Person> persons = Arrays.asList(
        new Person("Bill", "Gates"),
        new Person("Bill", "Steve"),
        new Person("Steve", "Jobs"),
        new Person("Steve", "Wozniac"));

// code above here

personByName.entrySet().forEach(System.out::println);

输出

Steve=[Steve Wozniac, Bill Steve, Steve Jobs]
Jobs=[Steve Jobs]
Bill=[Bill Steve, Bill Gates]
Wozniac=[Steve Wozniac]
Gates=[Bill Gates]

推荐阅读