Reputation: 569
I have a stream that processes some strings and collects them in a map.
But getting the following exception:
java.lang.IllegalStateException:
Duplicate key [email protected]
(attempted merging values [[email protected]] and [[email protected]])
at java.base/java.util.stream.Collectors.duplicateKeyException(Collectors.java:133)
I'm using the following code:
Map<String, List<String>> map = emails.stream()
.collect(Collectors.toMap(
Function.identity(),
email -> processEmails(email)
));
Upvotes: 1
Views: 8157
Reputation: 29048
The flavor of toMap()
you're using in your code (which expects only keyMapper
and valueMapper
) disallow duplicates merely because it's not capable to handle them. And exception message explicitly tells you that.
Judging by the resulting type Map<String, List<String>>
and by the exception message which shows strings enclosed in square brackets, it is possible to make the conclusion that processEmails(email)
produces a List<String>
(although it's not obvious from your description and IMO worth specifying).
There are multiple ways to solve this problem, you can either:
toMap(keyMapper,valueMapper,mergeFunction)
which requires the third argument, mergeFunction
- a function responsible for resolving duplicates.Map<String, List<String>> map = emails.stream()
.collect(Collectors.toMap(
Function.identity(),
email -> processEmails(email),
(list1, list2) -> list1 // or { list1.addAll(list2); return list1} depending on the your logic of resolving duplicates you need
));
groupingBy(classifier,downstream)
to preserve all the emails retrieved by processEmails()
that are associated with the same key by storing them into a List
. As a downstream collector we could utilize a combination of collectors flatMapping()
and toList()
.Map<String, List<String>> map = emails.stream()
.collect(Collectors.groupingBy(
Function.identity(),
Collectors.flatMapping(email -> processEmails(email).stream(),
Collectors.toList())
));
Note that the later option would make sense only if processEmails()
somehow generates different results for the same key, otherwise you would end up with a list of repeated values which doesn't seem to be useful.
But what you definitely shouldn't do in this case is to use distinct()
. It'll unnecessarily increase the memory consumption because it eliminates the duplicates by maintaining a LinkedHashSet
under the hood. It would be wasteful because you're already using Map
which is capable to deal with duplicated keys.
Upvotes: 3
Reputation: 845
try using
Collectors.toMap(Function keyFuntion, Function valueFunction, BinaryOperator mergeFunction)
You obviously have to write your own merge logic, a simple mergeFunction could be
(x1, x2) -> x1
Upvotes: 1
Reputation: 9175
You have duplicate emails. The toMap
version you're using explicitly doesn't allow duplicate keys. Use the toMap
that takes a merge function. How to merge those processEmails
results depends on your business logic.
Alternatively, use distinct()
before collecting, because otherwise you'll probably end up sending some people multiple emails.
Upvotes: 0