先初始化一个 School 的集合,然后将该集合转成一个 Map,key 为 id, value 为 name。
注:学校的 id 设置为重复的。
上代码:
public static void main(String[] args) {
List<School> schoolList = Lists.newArrayList(
new School("1", "a"),
new School("2", "b"),
new School("1", "c")
);
Map<String, School> schoolMap = schoolList.stream().collect(Collectors.toMap(School::getId, dic -> dic));
System.out.println(schoolMap);
}
@Data
@AllArgsConstructor
static class School implements Serializable {
private String id;
private String name;
}
运行以上代码,控制台会出现以下错误:
Exception in thread "main" java.lang.IllegalStateException: Duplicate key StreamTest.School(id=1, name=a)
at java.util.stream.Collectors.lambda$throwingMerger$0(Collectors.java:133)
at java.util.HashMap.merge(HashMap.java:1245)
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)
at com.iflytek.edu.udp.web.admin.manager.StreamTest.main(StreamTest.java:24)
原因就是转换为 map 的时候 key 重复了。
对于以上问题,是因为我们使用的方法不对,java8 的 Collectors 的 toMap 有三个重载方法:
public static Collector> toMap(Function super T, ? extends K> keyMapper,
Function super T, ? extends U> valueMapper)
public static Collector> toMap(Function super T, ? extends K> keyMapper,
Function super T, ? extends U> valueMapper,
BinaryOperator mergeFunction)
public static > Collector toMap(Function super T, ? extends K> keyMapper,
Function super T, ? extends U> valueMapper,
BinaryOperator mergeFunction,
Supplier mapSupplier)
上面的代码使用的就是第一个,我们看下该方法的注释就明白了:
返回一个将元素累积到 Map 中的收集器,其键和值是将提供的映射函数应用于输入元素的结果。
如果映射的键包含重复项(根据 Object.equals(Object)),则在执行集合操作时会抛出 IllegalStateException。 如果映射的键可能有重复项,请改用 toMap(Function, Function, BinaryOperator)。
所以答案就在里面,如果有映射的键有重复项就会报错,我们应该使用第二个方法,所以上面的代码我们略作修改即可,当碰到相同的键时,用后面的 value 覆盖前面的。
Map schoolMap = schoolList.stream().collect(Collectors.toMap(School::getId, dic -> dic, (oldValue, newValue) -> newValue));
代码跑起来,打印结果如下:
{1=StreamTest.School(id=1, name=c), 2=StreamTest.School(id=2, name=b)}