Use streams to merge List<Pair<A,B>> to List<Pair<A,List<B>>> based on key values

William Z :

I'm learning java 8 stream now, I'm curious is there any convenient way to convert List<Pair<A,B>> to List<Pair<A,List<B>>>, which is to merge values of pair by key?

I ever thought to use forEach method, but it seems less efficient since I need to traverse the new list to check the key of pair.

Samuel Philipp :

I would suggest using a LinkedHashMap (which also supports the insertion order of the entries) instead of List<Pair<>>. Using that you can simply use Collectors.groupingBy() and Collectors.mapping() to achieve that:

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())));

However if you really need a List<Pair<>> you can transform the map back to this:

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());

Bare in mind that you iterate twice with this solution (first the list, second the map).

Beside that, if you also want to remove duplicates of B you can use a LinkedHashSet (which also keeps the order) for that. For this you just need to change the mapping downstream collector:

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))));

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=95807&siteId=1