Home > OS >  Conditionally merging list of Java objects into a map via Stream API
Conditionally merging list of Java objects into a map via Stream API

Time:12-09

Java 11 here. I have a List<Foobar> as well as a Map<Foobar,List<String>>.

I would like to iterate over the list and:

  • if the current Foobar is a key in the map, and a specific string ("Can't please everyone") to that entry's value list
  • if the current Foobar is not a key in the map, and it as a new key, with a value that is an ArrayList consisting of a single string with the same value

I can accomplish this like so:

List<Foobar> foobarList = getSomehow();
Map<Foobar,List<String>> foobarMap = getItSomehow();
String msg = "Can't please everyone";
for (Foobar fb : foobarList) {
    if (foobarMap.containsKey(fb)) {
        foobarMap.get(fb).add(msg);
    } else {
        foobarMap.put(fb, Collections.singletonList(msg));
    }
}

This works great, but I'm trying to get this to work using the Java Stream API. My best attempt thus far:

List<Foobar> foobarList = getSomehow();
Map<Foobar,List<String>> foobarMap = getItSomehow();
String msg = "Can't please everyone";
foobarList.stream()
    .filter(fb -> foobarMap.containsKey(fb))
        .map(fb -> foobarMap.get(fb).add(msg))
    .filter(fb -> !foobarMap.containsKey(fb))
        .map(fb -> foobarMap.put(fb. Collections.singleton(msg));

Yields several compiler errors. Can anyone spot where I'm going awry?

CodePudding user response:

Streams are used either

  • To modify the contents of the stream elements, or
  • To produce another stream from it, or
  • To iterate over the elements and do something that doesn't affect the elements of this stream.

Since your use case is the last type, the logical operation is simply forEach(..). (I know it is a dampener :-), but that is how the use case is.)

foobarList.forEach( fb -> {
    if (foobarMap.containsKey(fb)) {
        foobarMap.get(fb).add(msg);
    } else {
         foobarMap.put(fb, Collections.singletonList(msg));
    }
} );

CodePudding user response:

As noticed by @Sree Kumar, you should use forEach().

However, I would suggest leveraging the Map.merge() method:

foobarList.forEach(fb -> foobarMap.merge(fb, Collections.singletonList(msg),
        (l1, l2) -> Stream.concat(l1.stream(), l2.stream()).toList()));
  • Related