- 实现步骤:
- 定义数据结构:我们需要一个
Map<Integer, List<String>>
来按长度分组字符串,同时还需要统计每个分组的字符串数量,所以可以使用Map<Integer, Map.Entry<List<String>, Long>>
,其中List<String>
是按字典序排序后的字符串列表,Long
是该分组的字符串数量。
- 实现自定义
Collector
:
supplier
方法:创建一个空的HashMap
用于存储分组结果。
accumulator
方法:将每个字符串按长度分组,并添加到对应的列表中。
combiner
方法:合并两个Map
,将相同长度分组的列表合并,并更新数量。
finisher
方法:对每个分组内的字符串列表按字典序排序,并统计数量。
characteristics
方法:返回Collector.Characteristics.IDENTITY_FINISH
和Collector.Characteristics.CONCURRENT
,表示finisher
方法返回的结果与累加器类型相同,并且该收集器可以在并行流中正确工作。
- 自定义
Collector
代码实现:
import java.util.*;
import java.util.stream.Collector;
public class StringLengthGroupingCollector implements Collector<String, Map<Integer, List<String>>, Map<Integer, Map.Entry<List<String>, Long>>> {
@Override
public Supplier<Map<Integer, List<String>>> supplier() {
return () -> new HashMap<>();
}
@Override
public BiConsumer<Map<Integer, List<String>>, String> accumulator() {
return (map, str) -> {
int length = str.length();
map.computeIfAbsent(length, k -> new ArrayList<>()).add(str);
};
}
@Override
public BinaryOperator<Map<Integer, List<String>>> combiner() {
return (map1, map2) -> {
map2.forEach((length, list) -> map1.computeIfAbsent(length, k -> new ArrayList<>()).addAll(list));
return map1;
};
}
@Override
public Function<Map<Integer, List<String>>, Map<Integer, Map.Entry<List<String>, Long>>> finisher() {
return map -> {
Map<Integer, Map.Entry<List<String>, Long>> result = new HashMap<>();
map.forEach((length, list) -> {
Collections.sort(list);
result.put(length, new AbstractMap.SimpleImmutableEntry<>(list, (long) list.size()));
});
return result;
};
}
@Override
public Set<Characteristics> characteristics() {
return Set.of(Collector.Characteristics.IDENTITY_FINISH, Collector.Characteristics.CONCURRENT);
}
}
- 使用示例:
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
List<String> strings = Arrays.asList("apple", "banana", "cherry", "date", "fig");
Map<Integer, Map.Entry<List<String>, Long>> result = strings.parallelStream()
.collect(new StringLengthGroupingCollector());
result.forEach((length, entry) -> {
System.out.println("Length: " + length);
System.out.println("Sorted Strings: " + entry.getKey());
System.out.println("Count: " + entry.getValue());
System.out.println();
});
}
}
- 自定义
Collector
中各个方法的作用:
supplier
:
- 作用:创建一个可变的结果容器,用于累加数据。在我们的例子中,它创建一个空的
HashMap
,用于后续按字符串长度分组存储字符串。
accumulator
:
- 作用:将输入元素添加到结果容器中。这里将每个字符串按其长度分组,添加到
HashMap
中对应长度的列表里。
combiner
:
- 作用:合并两个部分结果。在并行流中,会有多个部分结果需要合并,此方法将两个
Map
中相同长度分组的字符串列表合并在一起。
finisher
:
- 作用:对最终的结果容器进行转换,生成最终想要的结果类型。这里对每个分组内的字符串列表进行字典序排序,并统计数量,生成最终的
Map<Integer, Map.Entry<List<String>, Long>>
结果。
characteristics
:
- 作用:返回该收集器的特性。
IDENTITY_FINISH
表示finisher
方法返回的结果与累加器类型相同,CONCURRENT
表示该收集器可以在并行流中正确工作。