为什么 Collectors.toMap 报告值而不是重复键错误?

2022-09-01 18:47:36

这确实是一个关于小细节的问题,但我的印象是在这里出了什么问题。如果使用 Collectors.toMap 方法添加重复的键,则会引发异常,并显示消息“重复键”。为什么报告的是值而不是键?或者两者兼而有之?这真的很误导,不是吗?

这里有一个小测试来演示行为:

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class TestToMap {

    public static void main(String[] args) {

        List<Something> list = Arrays.asList(
            new Something("key1", "value1"),
            new Something("key2", "value2"),
            new Something("key3", "value3a"),
            new Something("key3", "value3b"));

        Map<String, String> map = list.stream().collect(Collectors.toMap(o -> o.key, o -> o.value));
        System.out.println(map);
    }

    private static class Something {
        final String key, value;

        Something(final String key, final String value){
            this.key = key;
            this.value = value;
        }
    }
}

答案 1

这是一个错误,请参阅JDK-8040892,并在Java 9中修复。阅读修复此问题的提交,新的异常消息将是

String.format("Duplicate key %s (attempted merging values %s and %s)", k, u, v)

其中 是重复的键,并且是映射到同一键的两个冲突值。kuv


答案 2

正如其他答案已经指出的那样,这是一个将在Java 9中修复的错误。出现错误的原因,是依赖于具有签名的Map.mergetoMap

V merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction)

此方法将插入 - 映射,如果以前没有映射,否则,将计算出新值。因此,如果不允许重复的密钥,那么直接提供一个将无条件抛出异常的密钥,然后就完成了。但。。。如果查看函数签名,您会注意到此函数仅接收要合并的两个,而不接收keyvaluekeyremappingFunctionremappingFunction

当为Java 8实现throwingMerger时,人们忽略了第一个参数不是关键,但更糟糕的是,它不是直接修复的。

您会注意到,如果您尝试使用重载的 toMap 收集器提供替代合并。此时,键值根本不在范围内。Java 9 开发人员必须更改整个实现,以便无重复的情况,以便能够提供报告受影响密钥的异常消息...toMap


推荐