Search code examples
javadictionarycollectionsjava-streamcollectors

Java streams is throwing java.lang.IllegalStateException: Duplicate key


I am a bit new to Java streams and struck in the following issue. I tried many things mentioned on this website and in the various articles but was unable to fix them so posting the question here.

I am creating a Map<String, Object> using the JavaStreams within my application and then I pass the value to the Map with duplicate keys then it throws the following error:

java.lang.IllegalStateException: Duplicate key namespace:localName (attempted merging values [ONE] and [TWO])

    at java.base/java.util.stream.Collectors.duplicateKeyException(Collectors.java:133)

Following is the method that's throwing this error:

public Map<String, Object> toMap() {
    final Map<String, Object> map = new HashMap<>();
    if (complex != null && complex.size() > 0) {
        final Map<String, Object> complexMap = complex.stream()
                .flatMap(c -> c.toMap().entrySet().stream())
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
        map.put(namespacePrefix.concat(":").concat(localName), complexMap);
    } else {
        map.put(namespacePrefix.concat(":").concat(localName), text);
    }
    return map;
}

I tried to make the Object as List so that if duplicate values are found then add it to the List something like this:

List<Object> values = (List<Object>) map.get(namespacePrefix.concat(":").concat(localName));
if (values == null) {
    values = new ArrayList<Object>();
}
values.add(text);
map.put(namespacePrefix.concat(":").concat(localName), values);

But this also does not seem to work for me. I want to retain both the values if the key is duplicates because I need all the values within my application. Can someone please explain to me what am I doing wrong here and some idea on how to fix it?


Solution

  • The duplicates are not allowed using Collectors.toMap(Function, Function) method:

    If the mapped keys contains duplicates (according to Object.equals(Object)), an IllegalStateException is thrown.

    Now it depends what output you prefer:

    • Map<String, Object>

      You need to use Collectors.toMap(Function, Function, BinaryOperator) with a merging function that decides, which value should be used when the same keys appear.

      If the mapped keys contains duplicates (according to Object.equals(Object)), the value mapping function is applied to each equal element, and the results are merged using the provided merging function.

      This solution requires knowing what to do with two or more duplicated keys.

    • Map<String, List<Object>>

      You need to use a grouping collector Collectors.groupingBy(Function) that groups all values by a key, i.e. former values appear in the List as value. If the former key was duplicated, the new Map would appear once and the List as a value would have multiple elements.

      Returns a Collector implementing a "group by" operation on input elements of type T, grouping elements according to a classification function, and returning the results in a Map.

      This solution adds you an option to identify duplicated fields and decide later on what to do with such values.