KKlalala
KKlalala

Reputation: 975

Java-Stream, toMap with duplicate keys

So there might be one abc for several payments, now I have:

//find abc id for each payment id
Map<Long, Integer> abcIdToPmtId = paymentController.findPaymentsByIds(pmtIds)
          .stream()
          .collect(Collectors.toMap(Payment::getAbcId, Payment::getPaymentId));

But then I reallize this could have duplicate keys, so I want it to return a

Map<Long, List<Integer>> abcIdToPmtIds 

which an entry will contain one abc and his several payments.

I know I might can use groupingBy but then I think I can only get Map<Long, List<Payments>> .

Upvotes: 13

Views: 13098

Answers (3)

ajain
ajain

Reputation: 510

Problem statement: Converting SimpleImmutableEntry<String, List<String>> -> Map<String, List<String>>.

For Instance you have a SimpleImmutableEntry of this form [A,[1]], [B,[2]], [A, [3]] and you want your map to looks like this: A -> [1,3] , B -> [2].

This can be done with Collectors.toMap but Collectors.toMap works only with unique keys unless you provide a merge function to resolve the collision as said in java docs.

https://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.html#toMap-java.util.function.Function-java.util.function.Function-java.util.function.BinaryOperator-

So the example code looks like this:

          .map(returnSimpleImmutableEntries)
          .collect(Collectors.toMap(SimpleImmutableEntry::getKey,
                                    SimpleImmutableEntry::getValue,
                                    (oldList, newList) -> { oldList.addAll(newList); return oldList; } ));

returnSimpleImmutableEntries method returns you entries of the form [A,[1]], [B,[2]], [A, [3]] on which you can use your collectors.

Upvotes: 4

fps
fps

Reputation: 34470

With Collectors.toMap:

Map<Long, Integer> abcIdToPmtId = paymentController.findPaymentsByIds(pmtIds)
    .stream()
    .collect(Collectors.toMap(
        Payment::getAbcId, 
        p -> new ArrayList<>(Arrays.asList(p.getPaymentId())),
        (o, n) -> { o.addAll(n); return o; }));

Though it's more clear and readable to use Collectors.groupingBy along with Collectors.mapping.

You don't need streams to do it though:

Map<Long, Integer> abcIdToPmtId = new HashMap<>();
paymentController.findPaymentsByIds(pmtIds).forEach(p ->
    abcIdToPmtId.computeIfAbsent(
            p.getAbcId(),
            k -> new ArrayList<>())
        .add(p.getPaymentId()));

Upvotes: 3

Louis Wasserman
Louis Wasserman

Reputation: 198461

Use the other groupingBy overload.

paymentController.findPaymentsByIds(pmtIds)
      .stream()
      .collect(
          groupingBy(Payment::getAbcId, mapping(Payment::getPaymentId, toList());

Upvotes: 18

Related Questions