Поток Java / сбор: сопоставление одного элемента с несколькими полями с несколькими ключами - PullRequest
5 голосов
/ 09 июля 2019

У меня есть следующий код, который я хотел бы попытаться написать с использованием Java-сборщиков.

Учитывая 2 атрибута (имя и фамилия) человека, я хотел бы получить карту, содержащую уникальное имя или фамилию в качестве ключа, и список соответствующих лиц.

Вот набор данных:

Person person1 = new Person();
person1.setFirstName("john");
person1.setLastName("doe");
person1.setUserId("user1");

Person person2 = new Person();
person2.setFirstName("doe");
person2.setLastName("frank");
person2.setUserId("user2");

Person person3 = new Person();
person3.setFirstName("john");
person3.setLastName("wayne");
person3.setUserId("user3");

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

Вывод (как и ожидалось) следующий:

frank=[Person{userId='user2', firstName='doe', lastName='frank'}], 

john=[Person{userId='user1', firstName='john', lastName='doe'}, Person{userId='user3', firstName='john', lastName='wayne'}], 

doe=[Person{userId='user1', firstName='john', lastName='doe'}, Person{userId='user2', firstName='doe', lastName='frank'}], 

wayne=[Person{userId='user3', firstName='john', lastName='wayne'}]

И код для заполнения карты:

Map<String, List<Person>> mapPersons = new HashMap<String, List<Person>>();
List<Person> listPersons;

for (Person p: personList) {
    if (mapPersons.get(p.getFirstName()) == null) {
        listPersons = new ArrayList<Person>();
        listPersons.add(p);
        mapPersons.put(p.getFirstName(), listPersons);
    } else {
        mapPersons.get(p.getFirstName()).add(p);
    }
    if (mapPersons.get(p.getLastName()) == null) {
        listPersons = new ArrayList<Person>();
        listPersons.add(p);
        mapPersons.put(p.getLastName(), listPersons);
    } else {
        mapPersons.get(p.getLastName()).add(p);
    }
}

Я не могу понять, как я могу получить имя или фамилию в качестве ключа (не так, как в Группировать по нескольким именам полей в Java 8 ). Должен ли я написать свой собственный сборщик?

1 Ответ

5 голосов
/ 09 июля 2019

Вы можете использовать Stream.flatMap() и Collectors.groupingBy() с Collectors.mapping():

Map<String, List<Person>> result = personList.stream()
        .flatMap(p -> Stream.of(p.getFirstName(), p.getLastName()).map(n -> new AbstractMap.SimpleEntry<>(n, p)))
        .collect(Collectors.groupingBy(Map.Entry::getKey, Collectors.mapping(Map.Entry::getValue, Collectors.toList())));

. При этом используется flatMap для расширения всех имен (первых и последних) до их объекта Person и его сбора.впоследствии.

В качестве альтернативы, используя Java 9 или выше, вы можете использовать Collectors.flatMapping():

Map<String, List<Person>> result = personList.stream()
        .collect(Collectors.flatMapping(
                p -> Stream.of(p.getFirstName(), p.getLastName()).map(n -> new AbstractMap.SimpleEntry<>(n, p)), 
                Collectors.groupingBy(Map.Entry::getKey, 
                        Collectors.mapping(Map.Entry::getValue, Collectors.toList()))));

Но я не думаю, что это более читабельно.

...