为什么java map collector会在原始对象上抛出重复键

Vic*_*Vic 5 java

当运行在JDK 8上编译的以下代码时,引发的异常是

java.lang.IllegalStateException:重复键1

这表示即使新密钥应该是与不同类型不同的对象,该异常仍然会提到原始对象.

List<Integer> ints = ImmutableList.of(1, 1);
Map<String, Integer> m = ints.stream()
    .collect(Collectors.toMap(intgr -> String.valueOf(intgr + 1), Function.identity()));
Run Code Online (Sandbox Code Playgroud)

映射函数可以是任意复杂的,完全不同的对象最终映射到同一个键,为什么会选择这种异常行为?

我的意思是为什么抛出的异常不是"Duplicate key 2"?

注意:在我们的例子中,映射的原始值是没有toString实现的第三方类,因此无法知道导致重复的原因.

Dea*_* Xu 5

这可能是一个 jdk 错误。我认为它可能会在更高版本中解决。(我使用的是 1.8_162)

你可以看到Collectors#throwingMerger

private static <T> BinaryOperator<T> throwingMerger() {
    return (u,v) -> { throw new IllegalStateException(String.format("Duplicate key %s", u)); };
}
Run Code Online (Sandbox Code Playgroud)

而输入参数u, v来自Map#merge. 它是旧值和新值。所以这是1, 1你的情况。但是,该错误信息是Duplicate key,这是完全错误的,因为既uv不关键,但值。

编辑

检查jdk 10,此错误已修复。

https://github.com/XDean/openjdk/blob/67672eec97164de10a9ca83ddbcef6b42816ed04/src/java.base/share/classes/java/util/stream/Collectors.java#L174

现在Collectors.toMap使用它自己的累加函数而不是使用Map.merge.

  • 它实际上已在 Java 9 中修复。请参阅 [this](https://bugs.openjdk.java.net/browse/JDK-8040892)。 (2认同)