Collectors.toMap() 抛出 “java.lang.IllegalStateException: Duplicate key ***” 异常。
Collectors.toMap() 抛出 “Duplicate key ***” 异常,异常堆栈如下:
1
2
3
4
5
6
7
8
9
10
11
|
java.lang.IllegalStateException: Duplicate key ***
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.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1374)
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)
|
错误分析:
这个错误信息表明在使用 Java Stream API 的 Collectors 进行流操作时遇到了一个问题。具体来说,java.util.stream.Collectors.lambda$throwingMerger 指的是在合并收集操作(比如toMap)中的元素时,由于使用了抛出异常的合并器(throwingMerger),而实际上出现了重复的键(key)。java.lang.IllegalStateException: Duplicate key *** 指出键 "***" 在收集过程中重复出现了,而合并策略是抛出 IllegalStateException 异常。
错误示例回放:
下面提供一个错误示例,该示例将抛出上述错误信息,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
package com.hxstrive.demo202406;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class Demo20240618143339 {
public static void main(String[] args) {
List<Item> getFunctionList = new ArrayList<>();
getFunctionList.add( new Item( "app1" , "应用1" ));
getFunctionList.add( new Item( "app2" , "应用2" ));
getFunctionList.add( new Item( "app1" , "应用3" ));
// 执行 toMap 操作
Map<String,String> map = getFunctionList.stream()
.collect(Collectors.toMap(Item::getAppId, Item::getType));
System.out.println(map);
}
// 内部类
private static class Item {
private String appId;
private String type;
public Item(String appId, String type) {
this .appId = appId;
this .type = type;
}
public String getAppId() {
return appId;
}
public void setAppId(String appId) {
this .appId = appId;
}
public String getType() {
return type;
}
public void setType(String type) {
this .type = type;
}
}
}
|
运行上述示例,抛出如下错误信息:
1
2
3
4
5
6
7
8
9
10
11
12
|
Exception in thread "main" java.lang.IllegalStateException: Duplicate key 应用1
at java.util.stream.Collectors.lambda$throwingMerger$0(Collectors.java:133)
at java.util.HashMap.merge(HashMap.java:1254)
at java.util.stream.Collectors.lambda$toMap$58(Collectors.java:1320)
at java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1382)
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 com.hxstrive.demo202406.Demo20240618143339.main(Demo20240618143339.java:21)
|
解决问题:
在 Java Stream API 中,Collectors.toMap 方法用于将流中的元素收集到一个 Map 中。如果你使用的是一个没有指定合并函数的 toMap 重载方法,并且流中的元素有重复的键,那么将会抛出 IllegalStateException 异常,因为默认的合并策略是不允许重复的。
为了解决这个问题,你可以提供一个合并函数,来定义当键冲突时如何合并值。例如:
1
2
3
4
5
|
Map<KeyType, ValueType> map = stream.collect(Collectors.toMap(
element -> element.getKey(), // 键的映射函数
element -> element.getValue(), // 值的映射函数
(existingValue, newValue) -> existingValue // 合并函数,决定如何处理重复的键
));
|
上述代码中,合并函数 (existingValue, newValue) -> existingValue 简单地保留了现有的值,并丢弃了新值。你可以根据需要自定义合并函数的行为。
重写示例 Demo20240618143339,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
package com.hxstrive.demo202406;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class Demo20240618143339 {
public static void main(String[] args) {
List<Item> getFunctionList = new ArrayList<>();
getFunctionList.add( new Item( "app1" , "应用1" ));
getFunctionList.add( new Item( "app2" , "应用2" ));
getFunctionList.add( new Item( "app1" , "应用3" ));
Map<String,String> map = getFunctionList.stream()
.collect(Collectors.toMap(Item::getAppId, Item::getType,
// 这是合并函数,保留旧值
(oldValue, newValue) -> oldValue));
System.out.println(map);
}
private static class Item {
private String appId;
private String type;
public Item(String appId, String type) {
this .appId = appId;
this .type = type;
}
public String getAppId() {
return appId;
}
public void setAppId(String appId) {
this .appId = appId;
}
public String getType() {
return type;
}
public void setType(String type) {
this .type = type;
}
}
}
|
运行示例,输出如下: