一、collect
方法详解
1. 核心功能
collect()
是 Java Stream API 的终端操作,用于将流中的元素累积到可变容器(如集合、Map)中。它提供了两种形式:
// 基础形式
<R> R collect(Supplier<R> supplier,
BiConsumer<R, ? super T> accumulator,
BiConsumer<R, R> combiner);
// 常用形式(使用Collector)
<R, A> R collect(Collector<? super T, A, R> collector);
2. 核心组件
当使用 Collector
时,包含三个关键组件:
-
Supplier:创建结果容器的工厂
-
Accumulator:将元素添加到容器的函数
-
Combiner:合并并行流结果的函数(用于并行处理)
3. 工作原理
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// 手动实现collect
List<String> result = names.stream().collect(
ArrayList::new, // Supplier: 创建容器
ArrayList::add, // Accumulator: 添加元素
ArrayList::addAll // Combiner: 合并结果
);
// 等价于
List<String> result = names.stream().collect(Collectors.toList());
二、Collectors.toMap
深度解析
1. 方法签名
// 基本形式
public static <T, K, U> Collector<T, ?, Map<K,U>> toMap(
Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper)
// 完整形式
public static <T, K, U, M extends Map<K, U>> Collector<T, ?, M> toMap(
Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction,
Supplier<M> mapSupplier)
2. 参数详解
参数 | 类型 | 说明 | 示例 |
---|---|---|---|
keyMapper | Function<T, K> | 从元素提取键的函数 | AttributePair::getKey |
valueMapper | Function<T, U> | 从元素提取值的函数 | AttributePair::getValue |
mergeFunction | BinaryOperator<U> | 键冲突时合并值的策略 | (v1, v2) -> v2 |
mapSupplier | Supplier<M> | 指定Map实现类的工厂 | TreeMap::new |
3. 使用场景示例
场景1:基本转换
List<AttributePair> pairs = Arrays.asList(
new AttributePair("color", "red"),
new AttributePair("size", "L")
);
Map<String, String> map = pairs.stream()
.collect(Collectors.toMap(
AttributePair::getKey,
AttributePair::getValue
));
// 结果: {color=red, size=L}
场景2:处理键冲突
List<AttributePair> pairs = Arrays.asList(
new AttributePair("color", "red"),
new AttributePair("color", "blue") // 重复键
);
// 保留最新值
Map<String, String> map1 = pairs.stream()
.collect(Collectors.toMap(
AttributePair::getKey,
AttributePair::getValue,
(oldVal, newVal) -> newVal
));
// 结果: {color=blue}
// 合并值
Map<String, String> map2 = pairs.stream()
.collect(Collectors.toMap(
AttributePair::getKey,
AttributePair::getValue,
(v1, v2) -> v1 + "," + v2
));
// 结果: {color=red,blue}
场景3:指定Map实现
// 使用TreeMap按键排序
Map<String, String> sortedMap = pairs.stream()
.collect(Collectors.toMap(
AttributePair::getKey,
AttributePair::getValue,
(v1, v2) -> v2,
TreeMap::new
));
4. 实现原理(简化版)
public static <T, K, U> Collector<T, ?, Map<K,U>> toMap(
Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper) {
return Collector.of(
HashMap::new, // Supplier
(map, element) -> {
K key = keyMapper.apply(element);
U value = valueMapper.apply(element);
if (map.putIfAbsent(key, value) != null) {
throw new IllegalStateException("Duplicate key");
}
},
(map1, map2) -> {
map1.putAll(map2);
return map1;
}
);
}
5. 关键注意事项
-
键冲突处理
-
默认情况下,遇到重复键会抛出
IllegalStateException
-
必须提供
mergeFunction
解决冲突
-
-
空值问题
-
键不能为
null
(会抛出NullPointerException
) -
值可以为
null
,但取决于具体 Map 实现
-
-
并行流支持
Map<String, String> parallelMap = pairs.parallelStream() .collect(Collectors.toMap( AttributePair::getKey, AttributePair::getValue, (v1, v2) -> v2 ));
-
性能优化
-
大集合使用
ConcurrentHashMap
提高并行性能
Map<String, String> concurrentMap = pairs.parallelStream() .collect(Collectors.toMap( AttributePair::getKey, AttributePair::getValue, (v1, v2) -> v2, ConcurrentHashMap::new ));
-
三、高级应用技巧
1. 嵌套映射
// 创建多级Map
Map<String, Map<String, String>> nestedMap = products.stream()
.collect(Collectors.toMap(
Product::getCategory,
p -> Map.of("name", p.getName(), "price", p.getPrice()),
(map1, map2) -> {
map1.putAll(map2);
return map1;
},
TreeMap::new
));
2. 复杂对象转换
// 值转换为复杂对象
Map<Long, ProductSummary> summaryMap = products.stream()
.collect(Collectors.toMap(
Product::getId,
p -> new ProductSummary(p.getName(), p.getPrice()),
(s1, s2) -> s1 // 处理冲突
));
3. 不可变Map
// Java 10+ 创建不可变Map
Map<String, String> immutableMap = pairs.stream()
.collect(Collectors.toUnmodifiableMap(
AttributePair::getKey,
AttributePair::getValue,
(v1, v2) -> v1 // 冲突处理
));
4. 分组统计
// 按类别分组并统计数量
Map<String, Long> categoryCount = products.stream()
.collect(Collectors.groupingBy(
Product::getCategory,
Collectors.counting()
));