为何Collectors.toMap无仅含键值映射与Map供应商的重载方法?
为什么Collectors.toMap没有「键映射+值映射+Map供应商」的重载?
我完全懂你的疑惑——当你确定键映射不会产生重复,又想使用HashMap以外的Map实现(比如LinkedHashMap来保留流的顺序)时,还要被迫传一个永远用不上的合并函数,确实显得有点冗余。
先明确下Collectors.toMap现有的三个重载变体:
- 基础键值映射:仅接收键和值的映射函数
toMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper) - 带重复键处理:在基础版之上增加合并函数,用来处理键重复时的冲突
toMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper, BinaryOperator<U> mergeFunction) - 自定义Map实现:在带合并函数的版本上,再增加一个Map供应商,用来指定返回的Map类型
toMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper, BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier)
你提到的场景确实很常见:能保证键唯一,只想自定义Map实现,但现有API里并没有对应的重载。目前要实现这个需求,有两种常见方式,但其中一种存在明显的坑:
方式1:传入占位符合并函数(推荐)
既然带供应商的重载必须要合并函数,那我们可以传一个简单的lambda作为占位符(比如直接返回第一个值)——因为确定不会有重复键,这个函数永远不会被执行:
persons.stream() .collect(Collectors.toMap(Person::getId, Function.identity(), (a, b) -> a, LinkedHashMap::new));
这种方式是可行的,而且能正常达到自定义Map实现的目的(比如用LinkedHashMap保留流的原有顺序)。
方式2:尝试用collectingAndThen(不推荐)
有人会想到先收集成默认的HashMap,再转换成目标Map类型,比如:
persons.stream() .collect(Collectors.collectingAndThen(Collectors.toMap(Person::getId, Function.identity()), LinkedHashMap::new));
但这里要特别注意:正如@Holger指出的,这种方式完全达不到保留顺序的目的!因为toMap默认用HashMap填充,而HashMap是无序的,后续把它复制到LinkedHashMap只是把无序的键值对转移过去,根本没法恢复流的原有顺序,所以这种方法是无效的。
内容的提问来源于stack exchange,提问作者Thiyagu
相关产品推荐
相关产品推荐

