Collectors.toMap具有相同的键(打印相同的键)

lia*_*ajn 6 java java-8 collectors

我有这个代码来获取地图:

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
    )
);
Run Code Online (Sandbox Code Playgroud)

如何用重复键打印消息"重复键"?

Hol*_*ger 12

正如本回答所解释的,这是一个已知的问题,将在Java 9中修复 - 至少对于toMap不接受合并函数的收集器而言.

由于merge函数只接收要合并的两个值,并且签名不能轻易更改,因此无法解决这些重载方法.不幸的是,没有toMap收集器接受Map Supplier没有显式合并功能的收集器,所以除非这个版本在发布之前发生变化,否则将无法修复LinkedHashMap应该返回的场景.

所以解决方案是实现自己的收集器.然后,您不必等待Java 9,也不必冒险失望.

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));
}
Run Code Online (Sandbox Code Playgroud)

您可以将此收集器用作

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

或使用合格的MyCollector.toMap,指的是您放置该自定义收集器的类.


Nic*_*tto 5

如何用重复键打印消息"重复键"?

使用当前代码,您将获得消息"重复键",其中列表中MyObject包含至少2个MyObject具有相等对象作为值getKey()的实例,例如Arrays.asList(new MyObject("foo", "bar"), new MyObject("foo", "bar2")).

如何获得相应的密钥?

到目前为止,无法获得相应的密钥,您当前从合并函数获得的内容实际上是使用相同的密钥映射的2个值,这些密钥需要合并以仅保留相应密钥的一个值.

您的问题是Java 9中已修复的已知问题,请参阅JDK-8040892以获取更多详细信息,相应的修复将允许我们从合并函数中获取要合并的键和值.

另请参见为什么Collectors.toMap在重复键错误上报告值而不是键?

  • 请注意,修复只会在没有显式合并功能的情况下影响`toMap`收集器.由于仍然没有`toMap`收集器接受没有自定义合并功能的自定义地图供应商,您仍然无法使用显式地图类型(如`LinkedHashMap`)并同时报告重复键... (3认同)