Используйте потоки, чтобы объединить список> к списку> на основе ключевых значений - PullRequest
3 голосов
/ 19 мая 2019

Сейчас я изучаю поток Java 8, мне интересно, есть ли какой-нибудь удобный способ конвертировать List<Pair<A,B>> в List<Pair<A,List<B>>>, который заключается в объединении значений пары по ключу?

Я когда-либо думал использовать forEach метод, но он кажется менее эффективным, так как мне нужно просмотреть новый список, чтобы проверить ключ пары.

1 Ответ

3 голосов
/ 19 мая 2019

Я бы предложил использовать LinkedHashMap (который также поддерживает порядок вставки записей) вместо List<Pair<>>. Используя это, вы можете просто использовать Collectors.groupingBy() и Collectors.mapping() для достижения этого:

List<Pair<A, B>> list = ...;
Map<A, List<B>> result = list.stream()
        .collect(Collectors.groupingBy(Pair::getKey, LinkedHashMap::new, 
                Collectors.mapping(Pair::getValue, Collectors.toList())));

Однако, если вам действительно нужен List<Pair<>>, вы можете преобразовать карту обратно в это:

List<Pair<A, B>> list = ...;
List<Pair<A, List<B>>> result = list.stream()
        .collect(Collectors.groupingBy(Pair::getKey, LinkedHashMap::new, 
                Collectors.mapping(Pair::getValue, Collectors.toList())))
        .entrySet().stream()
        .map(e -> new Pair<>(e.getKey(), e.getValue()))
        .collect(Collectors.toList());

Имейте ввиду, что вы повторяете дважды с этим решением (сначала список, затем карта).

Кроме того, если вы также хотите удалить дубликаты B, вы можете использовать для этого LinkedHashSet (который также сохраняет порядок). Для этого вам просто нужно изменить отображающий коллектор ниже по потоку:

List<Pair<A, B>> list = ...;
Map<A, Set<B>> result = list.stream()
        .collect(Collectors.groupingBy(Pair::getKey, LinkedHashMap::new, 
                Collectors.mapping(Pair::getValue, Collectors.toCollection(LinkedHashSet::new))));
...