Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I avoid IllegalStateException: Duplicate key in Java Streams? [duplicate]

I'm trying to convert a list of objects to a Map using Java Streams:

List<String> list = List.of("a", "b", "a");
Map<String, Integer> map = list.stream()
    .collect(Collectors.toMap(s -> s, String::length));

This throws:

java.lang.IllegalStateException: Duplicate key a (attempted merging values 1 and 1)

I expected it to just create a map with unique keys, but I forgot that my list has duplicates. What's the correct way to handle this if duplicates may occur? Is there a way to merge them, or just keep the first one?

like image 879
Vitalii Avatar asked Aug 25 '26 05:08

Vitalii


2 Answers

Make sure you have 'distinct' values:

Map<String, Integer> map = list.stream()
    .distinct()
    .collect(Collectors.toMap(s -> s, String::length));
like image 91
Nick Holt Avatar answered Aug 26 '26 21:08

Nick Holt


It throws the exception because the Collector does not know what to do with the duplicates in the merge operation. In its internal it will use:

    /**
     * {@code BiConsumer<Map, T>} that accumulates (key, value) pairs
     * extracted from elements into the map, throwing {@code IllegalStateException}
     * if duplicate keys are encountered.
     *
     * @param keyMapper a function that maps an element into a key
     * @param valueMapper a function that maps an element into a value
     * @param <T> type of elements
     * @param <K> type of map keys
     * @param <V> type of map values
     * @return an accumulating consumer
     */
    private static <T, K, V>
    BiConsumer<Map<K, V>, T> uniqKeysMapAccumulator(Function<? super T, ? extends K> keyMapper,
                                                    Function<? super T, ? extends V> valueMapper) {
        return (map, element) -> {
            K k = keyMapper.apply(element);
            V v = Objects.requireNonNull(valueMapper.apply(element));
            V u = map.putIfAbsent(k, v);
            if (u != null) throw duplicateKeyException(k, u, v);
        };
    }

Hence the exception you are seeing. To handle it you can just tell it with a merge function what to do in case of a duplicate:

Map<String, Integer> map = 
     list.stream()
         .collect(Collectors.toMap(
                s -> s,
                String::length,
                (a1, a2) -> a1
            ));

This uses the implementation from its [Java Doc](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-)

public static <T,K,U> Collector<T,?,Map<K,U>> toMap(Function<? super T,? extends K> keyMapper,
                                                    Function<? super T,? extends U> valueMapper,
                                                    BinaryOperator<U> mergeFunction)
like image 41
Jorge Campos Avatar answered Aug 26 '26 23:08

Jorge Campos



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!