Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Merging Map streams using Java 8 Lambda Expression

I have two maps m1 and m2 of type Map<Integer, String>, which has to be merged into a single map Map<Integer, List<String>>, where values of same keys in both the maps are collected into a List and put into a new Map.

Solution based on what I explored:

Map<Integer, List<String>> collated =
        Stream.concat(m1.entrySet().stream(), m2.entrySet().stream()).collect(
                Collectors.toMap(Entry::getKey,
                        Entry::getValue, (a, b) -> {
                            List<String> merged = new ArrayList<String>(a);
                            merged.addAll(b);
                            return merged;
                        }));

But, this solution expects source List to be Map<Integer, List<String>> as the merge function in toMap expects operands and result to be of the same type.

I don't want to change the source collection. Please provide your inputs on achieving this using lambda expression.

like image 709
viktor Avatar asked Jul 18 '15 06:07

viktor


2 Answers

That's a job for groupingBycollector:

Stream.of(m1,m2)
        .flatMap(m->m.entrySet().stream())
        .collect(groupingBy(
                Map.Entry::getKey,
                mapping(Map.Entry::getValue, toList())
        ));
like image 92
Misha Avatar answered Sep 27 '22 17:09

Misha


I can't test it right now, but I think all you need it to change the mapping of the value from Entry::getValue to a List that contains that value :

Map<Integer, List<String>> collated =
        Stream.concat(m1.entrySet().stream(), m2.entrySet().stream())
                .collect(Collectors.toMap(Entry::getKey,
                        e -> {
                            List<String> v = new ArrayList<String>();
                            v.add(e.getValue());
                            return v;
                        },
                        (a, b) -> {
                            List<String> merged = new ArrayList<String>(a);
                            merged.addAll(b);
                            return merged;
                        }));

EDIT: The idea was correct. The syntax wasn't. Current syntax works, though a bit ugly. There must be a shorter way to write it.

You can also replace e -> {..} with e -> new ArrayList<String>(Arrays.asList(new String[]{e.getValue()})).

or with e -> Stream.of(e.getValue()).collect(Collectors.toList())

Or you can do it with groupingBy :

Map<Integer, List<String>> collated =
        Stream.concat(m1.entrySet().stream(), m2.entrySet().stream())
                .collect(Collectors.groupingBy(Map.Entry::getKey,
                        Collectors.mapping(Map.Entry::getValue,
                                Collectors.toList())));
like image 39
Eran Avatar answered Sep 27 '22 18:09

Eran