Создание потока List of List (вложенный список) с использованием forEach, Java 8

class EntityCompositeId {
    private Long firstId;
    private Long secondId;
    // getter & setter...
}

class EntityComposite {
    private EntityCompositeId id;
    private String first;
    private String second;
    // getter & setter...
}

List<EntityComposite> listEntityComposite = ....
Supose this content

1, 1, "firstA", "secondBirdOne"
1, 2, "firstA", "secondBirdTwo"
1, 3, "firstA", "secondBirdThree"

2, 1, "firstB", "secondCatOne"
2, 2, "firstB", "secondCatTwo"
2, 3, "firstB", "secondCatThree"

3, 1, "firstC", "secondDogOne"
3, 2, "firstC", "secondDogTwo"
3, 3, "firstC", "secondDogThree"

Map<Long, List<String>> listOfLists = new HashMap<>();

Теперь с помощью потока я хочу заполнить как:

 1 -> {"secondBirdOne", "secondBirdTwo", "secondBirdThree"}
 2 -> {"secondCatOne", "secondCatTwo", "secondCatThree"}
 3 -> {"secondDogOne", "secondDogTwo", "secondDogThree"}

Мой незаконченный (вот в чем вопрос) код:

listEntityComposite.stream()forEach(entityComposite {
        // How create a list according entityComposite.getId.getFirstId()?
        listOfLists.put(entityComposite.getId.getFirstId(), .... )
    });

3 ответа

Решение

Есть несколько разных подходов, в которых вы можете выполнить поставленную задачу.

forEach + computeIfAbsent

 Map<Long, List<String>> map = new HashMap<>();
 listEntityComposite.forEach(e -> map.computeIfAbsent(e.getId().getFirstId(), 
                k -> new ArrayList<>()).add(e.getSecond()));
  • перечисляет элементы в listEntityComposite с помощью forEach
  • для каждого элемента использует computeIfAbsent вычислить ключ (т.е. firstId) и стоимость (то есть List<String>)

groupingBy + отображение

Другой подход заключается в применении groupingBy с mapping нижний коллектор:

Map<Long, List<String>> resultSet = listEntityComposite.stream()
                .collect(groupingBy(e -> e.getId().getFirstId(),
                        mapping(EntityComposite::getSecond, toList())));
  • группирует исходные элементы по функции классификации e.getId().getFirstId() а затем применяет mapping нижестоящий коллектор для дальнейшего уточнения нашего запроса.

forEach + слияние

listEntityComposite.forEach(e -> map.merge(e.getId().getFirstId(),
                new ArrayList<>(singletonList(e.getSecond())),
                (l, r) -> {l.addAll(r); return l;}));
  • перечисляет элементы в listEntityComposite с помощью forEach

  • для каждого элемента использует merge вычислить ключ (т.е. firstId) и стоимость (то есть List<String>)

на карту

listEntityComposite.stream()
                   .collect(toMap(e -> e.getId().getFirstId(), 
                             v ->  new ArrayList<>(singletonList(v.getSecond())),
                             (l, r) -> {l.addAll(r); return l;}));
  • применяет keyMapper функция e -> e.getId().getFirstId() извлечь ключи карты.
  • применяет valueMapper функция v -> new ArrayList<>(singletonList(v.getSecond())) извлечь значения карты.
  • применяет merge функция (l, r) -> {l.addAll(r); return l;} разрешать ключевые коллизии.

В заключение, forEach + computeIfAbsent подход и groupingBy + mapping Подход - это то, что вам следует отдать предпочтение в этом конкретном случае, поскольку они более идиоматичны.

collect является более подходящей терминальной операцией для генерации выхода Map чем forEach,

Ты можешь использовать collect() с Collectors.groupingBy:

Map<Long, List<String>> listOfLists =
    listEntityComposite.stream()
                       .collect(Collectors.groupingBy(e -> e.getId().getFirstId(),
                                                      Collectors.mapping(EntityComposite::getSecond,
                                                                         Collectors.toList());

Collectors.groupingBy с одним аргументом (просто e -> e.getId().getFirstId()) будет генерировать Map<Long,List<EntityComposite>>,

Привязать к нему Collectors.mapping() карты каждый EntityComposite экземпляр к соответствующему getSecond()String, как требуется.

Я бы посоветовал взглянуть на MultiMap от Guava, который облегчает работу с вашим вариантом использования (предоставляя множество оптимизаций и дополнительную функциональность, которую вы, возможно, захотите получить позже в будущем)

Редактировать: пример того, почему Multimap имеет больше смысла, чем, скажем, использование подхода computeIfAbsent: 1. у каждого ключа есть список определенного размера, что если вы захотите получить "общий" размер в будущем? вам нужно было бы создать некоторую логику для достижения этой цели с хорошей производительностью (или использовать метод, который использует O(ключи)) 2. в данный момент вы только помещаете вещи в карту, но что произойдет, если вы хотите удалить вещи из карта в будущем? Вам нужно будет написать некоторый шаблонный код (который легко ошибиться), чтобы убедиться, что удаление значений не вызывает утечку памяти

Есть и другие преимущества использования мультикарты, но это всего лишь два легко объяснимых преимущества.

Редактировать 2: пример с использованием вашего ввода:

import java.util.Arrays;
import java.util.List;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;

public class Example {

    public static class EntityCompositeId {
        @Override
        public String toString() {
            return "EntityCompositeId [firstId=" + firstId + ", secondId=" + secondId + "]";
        }

        public EntityCompositeId(Long firstId, Long secondId) {
            super();
            this.firstId = firstId;
            this.secondId = secondId;
        }

        private Long firstId;

        public Long getFirstId() {
            return firstId;
        }

        private Long secondId;
    }

    public static class EntityComposite {
        @Override
        public String toString() {
            return "EntityComposite [id=" + id + ", first=" + first + ", second=" + second + "]";
        }

        public EntityComposite(EntityCompositeId id, String first, String second) {
            super();
            this.id = id;
            this.first = first;
            this.second = second;
        }

        private EntityCompositeId id;

        public EntityCompositeId getId() {
            return id;
        }

        private String first;
        private String second;
    }

    public static void main(String[] args) {
        List<EntityComposite> listEntityComposite = Arrays.asList(
                new EntityComposite(new EntityCompositeId(1l, 1l), "firstA", "secondBirdOne"),
                new EntityComposite(new EntityCompositeId(1l, 2l), "firstA", "secondBirdTwo"),
                new EntityComposite(new EntityCompositeId(1l, 3l), "firstA", "secondBirdThree"),
                new EntityComposite(new EntityCompositeId(2l, 1l), "firstB", "secondCatOne"),
                new EntityComposite(new EntityCompositeId(2l, 2l), "firstB", "secondCatTwo"),
                new EntityComposite(new EntityCompositeId(2l, 3l), "firstB", "secondCatThree"),
                new EntityComposite(new EntityCompositeId(3l, 1l), "firstC", "secondDogOne"),
                new EntityComposite(new EntityCompositeId(3l, 2l), "firstC", "secondDogTwo"),
                new EntityComposite(new EntityCompositeId(3l, 3l), "firstC", "secondDogThree"));
        ListMultimap<Long, EntityComposite> map = ArrayListMultimap.create();
        listEntityComposite.forEach(entityComposite -> map.put(entityComposite.getId().getFirstId(), entityComposite));
        map.keySet().forEach(key -> System.out.println(map.get(key)));
    }
}

Получает следующий вывод:

[EntityComposite [id=EntityCompositeId [firstId=1, secondId=1], first=firstA, second=secondBirdOne], EntityComposite [id=EntityCompositeId [firstId=1, secondId=2], first=firstA, second=secondBirdTwo], EntityComposite [id=EntityCompositeId [firstId=1, secondId=3], first=firstA, second=secondBirdThree]]
[EntityComposite [id=EntityCompositeId [firstId=2, secondId=1], first=firstB, second=secondCatOne], EntityComposite [id=EntityCompositeId [firstId=2, secondId=2], first=firstB, second=secondCatTwo], EntityComposite [id=EntityCompositeId [firstId=2, secondId=3], first=firstB, second=secondCatThree]]
[EntityComposite [id=EntityCompositeId [firstId=3, secondId=1], first=firstC, second=secondDogOne], EntityComposite [id=EntityCompositeId [firstId=3, secondId=2], first=firstC, second=secondDogTwo], EntityComposite [id=EntityCompositeId [firstId=3, secondId=3], first=firstC, second=secondDogThree]]
Другие вопросы по тегам