使用Collectors.toMap处理具有相同键的情况(打印相同的键)

6

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

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
    )
);

如何在重复键时打印消息“Duplicate keys”?


我想在控制台中输出消息“重复的键”和键值,但我不知道如何实现。 - liarkajn
2个回答

14
此答案所述,这是一个已知问题,将在Java 9中得到解决——至少对于不接受合并函数的toMap收集器。

由于合并函数仅接收要合并的两个值且签名不能轻易更改,因此这些重载方法没有解决方案。不幸的是,没有接受MapSupplier而不带显式合并函数的toMap收集器,因此除非在发布之前发生变化,否则您需要返回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));
}

您可以将此收集器用作:
LinkedHashMap<KeyType, ValueType> map = myList.stream()
    .collect(toMap(MyObject::getKey, MyObject::getValue, LinkedHashMap::new));

或者使用一个合格的 MyCollector.toMap,引用您放置自定义收集器的类。

谢谢,收集器接口对我来说有点不清楚,这是一个很好的学习方式! - Guillaume

6

如何在重复键时打印消息“Duplicate keys”?

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

如何获取相应的键?

到目前为止,无法获取相应的键,您当前从合并函数中获得的实际上是使用相同键映射的2个值,需要合并以仅保留相应键的一个值。

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

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


3
请注意,此修复仅适用于toMap收集器没有显式合并功能的情况。由于仍然没有接受自定义映射供应程序而不带自定义合并功能的toMap收集器,因此您仍将无法同时使用显式映射类型(如LinkedHashMap)并报告重复键... - Holger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接