Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Collectors.toMap with same keys(print same key)

I have this code to get map:

List<MyObject> myList = myMethod.getList();
myList.stream().collect(
    Collectors.toMap(
        MyObject::getKey, 
        MyObject::getValue,
        (e1, e2) -> {
            System.out.println("Duplicate keys !!!");
            return e1;
        }, 
        LinkedHashMap::new
    )
);

how i can print message "Duplicate keys" with duplicate key?

like image 551
liarkajn Avatar asked Nov 23 '16 10:11

liarkajn


People also ask

What happens when HashMap duplicate keys?

HashMap stores key, value pairs and it does not allow duplicate keys. If the key is duplicate then the old key is replaced with the new value.

How do I fix Java Lang IllegalStateException duplicate key?

lang. IllegalStateException exception. To deal with the duplicate key introduce the mergeFunction as another argument. mergeFunction will resolve the duplicate key issue.

How do I store duplicate keys on a map?

You can use a TreeMap with a custom Comparator in order to treat each key as unequal to the others. It would also preserve the insertion order in your map, just like a LinkedHashMap. So, the net result would be like a LinkedHashMap which allows duplicate keys!

Can TreeMap have duplicate keys?

A TreeMap cannot contain duplicate keys. TreeMap cannot contain the null key. However, It can have null values.


2 Answers

As explained in this answer, this is a known issue that is going to be fixed in Java 9—at least for the toMap collector that doesn’t accept a merge function.

Since the merge function only receives the two values to be merged and signature can’t be easily changed, there is no fix for these overloaded methods in sight. Unfortunately, there is no toMap collector which accepts a Map Supplier without an explicit merge function, so unless this will change until the release, there will be no fix for your scenario where a LinkedHashMap should be returned.

So the solution is to implement your own collector. Then, you don’t have to wait for Java 9 and don’t risk to get disappointed.

static <T, K, V, M extends Map<K, V>> Collector<T, ?, M> toMap(
    Function<T, K> keyExtractor, Function<T, V> valueExtractor, Supplier<M> mapSupplier) {
    return Collector.of(mapSupplier,
        (m, t) -> putUnique(m, keyExtractor.apply(t), valueExtractor.apply(t)),
        (m1,m2)-> { m2.forEach((k, v) -> putUnique(m1, k, v)); return m1; }
    );
}
private static <K,V> void putUnique(Map<K,V> map, K key, V v1){
    V v2 = map.putIfAbsent(key, v1);
    if(v2 != null) throw new IllegalStateException(
        String.format("Duplicate key %s (values %s and %s)", key, v1, v2));
}

You can use this collector as

LinkedHashMap<KeyType, ValueType> map = myList.stream()
    .collect(toMap(MyObject::getKey, MyObject::getValue, LinkedHashMap::new));

or use a qualified MyCollector.toMap, referring to the class where you put that custom collector.

like image 165
Holger Avatar answered Sep 30 '22 07:09

Holger


how i can print message "Duplicate keys" with duplicate key?

With your current code, you will get the message "Duplicate keys" with a list of MyObject which contains at least 2 instances of MyObject that have equal objects as value for getKey(), for example Arrays.asList(new MyObject("foo", "bar"), new MyObject("foo", "bar2")).

How to get the corresponding key?

Up to now it is not possible to get the corresponding key, what you currently get from the merge function are actually 2 values mapped with the same key that need to be merged to keep only one value for the corresponding key.

Your problem is a known issue that is fixed in Java 9 see JDK-8040892 for more details, the corresponding fix will allow us to get from the merge function both the key and the values to merge.

See Also Why does Collectors.toMap report value instead of key on Duplicate Key error?

like image 39
Nicolas Filotto Avatar answered Sep 30 '22 08:09

Nicolas Filotto