这真是一个关于细微问题的问题,但我的印象是在这里弄错了。如果使用Collectors.toMap-method添加重复键,则会抛出一条带有“重复键”消息的异常。为什么报告的价值而不是关键?甚至两个?这真的很误导,不是吗?
这是一个证明行为的小测试:
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;
}
}
}
答案 0 :(得分:17)
这被报告为一个错误,请参阅commit fixing this,并在Java 9中修复。阅读{{3}},新的异常消息将是
String.format("Duplicate key %s (attempted merging values %s and %s)", k, u, v)
其中k
是重复键,u
和v
是映射到同一键的两个冲突值。
答案 1 :(得分:7)
正如其他答案已经说明的那样,这是一个将在Java 9中修复的错误。错误产生的原因是,toMap
依赖于具有签名的Map.merge
V merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction)
如果key
之前没有映射,则此方法将插入value
- key
映射,否则将评估remappingFunction
以计算新值值。因此,如果不允许重复键,则可以直接提供remappingFunction
,无条件地抛出异常,然后就完成了。但是......如果你看一下函数签名,你会注意到这个函数只接收要合并的两个值,而不是 key 。
当为Java 8实现throwingMerger
时,忽略了第一个参数是而不是密钥,但更糟糕的是,它不是直接修复的。
如果您尝试使用overloaded toMap
collector提供替代合并,您会注意到。此时关键值不在范围内。 Java 9开发人员必须更改无重复案例的整个toMap
实现,以便能够提供报告受影响密钥的异常消息...
答案 2 :(得分:3)
这是Jdk 8中的一个已知错误。抛出的消息应该显示至少两个存在密钥冲突的值,或者理想情况下显示发生冲突的密钥。附件是相同
的link答案 3 :(得分:0)
如果您遇到此问题并被迫在项目中使用Java 8,则必须创建自己的收集器才能获得有意义的错误消息。
下面您可以找到一个收集器的示例,该示例为您提供了一条有意义的消息,类似于使用Java 9所获得的消息:
private static class Something {
private final String k;
private final String v;
public Something(String k, String v) {
this.k = k;
this.v = v;
}
}
public static void main(String[] args) throws IOException, ParseException {
List<Something> list = Arrays.asList(
new Something("key1", "value1"),
new Something("key2", "value2"),
new Something("key3", "value3a"),
new Something("key3", "value3b"));
Collector<Something, ?, Map<String, String>> eloquentCollector =
Collector.of(HashMap::new, (map, something) -> putUnique(map, something.k, something.v),
(m1, m2) -> { m2.forEach((k, v) -> putUnique(m1, k, v)); return m1; });
Map<String, String> map = list.stream().collect(eloquentCollector);
System.out.println(map);
}
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' (attempted merging incoming value '%s' with existing '%s')", key, v1, v2));
}
如果运行此代码,您将看到以下错误消息,指向重复的键并合并值:
重复键“ key3”(尝试合并值value3a和value3b)
如果您在以上parallelStream()
的示例中使用,您应该得到相同的异常消息,例如:
Map<String, String> map = list.parallelStream().collect(eloquentCollector);
要感谢Holger指出并行流的原始答案不正确。
答案 4 :(得分:0)
在Java 8中,不允许一次将来自两个流(或具有相同strem的迭代两次)的重复值合并到Map中。
您可以使用 Collectors.groupingBy 和 Collectors.mapping 将结果合并到Map<Object, Set<Object>>
或
Map<Object, List<Object>>
进行处理。
List<Employee> employees = new ArrayList<>();
employees.add(new Employee("Sachin", 40));
employees.add(new Employee("Sachin", 30));
employees.add(new Employee("Rahul", 30));
Map<Integer, Set<String>> map = employees.stream()
.collect(
Collectors.groupingBy(
Employee::getAge,
Collectors.mapping(
Employee::getName,
Collectors.toSet()
)
)
);
答案 5 :(得分:0)
是的。它已在 Java 9 上修复。 Java 8 中的消息。
Exception in thread "main" java.lang.IllegalStateException: Duplicate key HELLO IM NOT A KEY I AM A VALUE #1
at java.util.stream.Collectors.lambda$throwingMerger$0(Collectors.java:133)
at java.util.HashMap.merge(HashMap.java:1253)
at java.util.stream.Collectors.lambda$toMap$58(Collectors.java:1320)
at java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
at compass.issues.CollectorsToMapMessageIsWrong.main(CollectorsToMapMessageIsWrong.java:31)
Java >=9 消息是不言自明的。
例如在 Java 15 中我可以得到。
Exception in thread "main" java.lang.IllegalStateException: Duplicate key 1 (attempted merging values HELLO IM NOT A KEY I AM A VALUE #1 and HELLO IM NOT A KEY I AM A VALUE #2)
at java.base/java.util.stream.Collectors.duplicateKeyException(Collectors.java:133)
at java.base/java.util.stream.Collectors.lambda$uniqKeysMapAccumulator$1(Collectors.java:180)
at java.base/java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
at java15.CollectorsToMapMessageIsWrong.main(CollectorsToMapMessageIsWrong.java:31)
答案 6 :(得分:0)
BinaryOperator<Long> mergeFunction = (oldValue, newValue) -> newValue;
Map<String, Long> dataTableIdMap = dataTableRepository.findByClientId(clientid)
.stream()
.collect(Collectors.toMap(DataTable::getAbbreviation, DataTable::getId, mergeFunction));