流的收集器非常有用。可用于将流元素归约和汇总为一个值 ,元素分组,元素分区。
1.归约与汇总
静态导入java.util.stream.Collectors.*。
1.1求值
Collectors.maxBy和Collectors.minBy,来计算流中的最大或最小值。参数为Comparator。
Optional<A> max = Stream.of(new A(4),new A(2),new A(8)).collect(maxBy(comparing(A::getNum)));
Optional<A> min = Stream.of(new A(4),new A(2),new A(8)).collect(minBy(comparing(A::getNum)));
max.ifPresent(a->System.out.println(a.getNum()));
min.ifPresent(a->System.out.println(a.getNum()));
Collectors.summingInt
。求和,输出int 。还有summingLong和SummingDouble方法。
int sum = Stream.of(new A(4),new A(2),new A(8)).collect(summingInt(A::getNum));<!--输出14-->
Collectors.averagingInt 求平均值。还有averagingLong和averagingDouble。
Double ave = Stream.of(new A(4),new A(2),new A(8)).collect(averagingInt(A::getNum));
summarizingInt方法返回的值中包含了最大值,最小值,和,平均值,数量。还有summarizingLong和summarizingDouble。
IntSummaryStatistics iss = Stream.of(new A(4),new A(2),new A(8)).collect(summarizingInt(A::getNum));
1.2连接字符串
joining工厂方法返回的收集器会把对流中每一个对象应用toString方法得到的所有字符串连接成一个字符串。joining参数为分隔符。
List<String> list = Arrays.asList("hello","world","stream");
String s = list.stream().collect(joining(", "));
reducing方法,可以有初始值,转换函数,累加函数。
List<A> list = Arrays.asList(new A(5),new A(2),new A(3));
int i = list.stream().collect(reducing(0, A::getNum,(a,b)->a+b));
2.分组
Collectors.groupingBy可以用于分组。返回Map类型。参数为function。还有个重载方法参数为Function和Collector,Collector参数就是对前面分完组的内容进行操作。
List<A> list = Arrays.asList(new A("tom",22),new A("james",22),new A("jack",3));
Map<String, List<A>> map1 = list.stream().collect(groupingBy(A::getName));
多级分组:
List<A> list = Arrays.asList(new A("tom",22),new A("tom",15),new A("jack",3));
Map<String, Map<String,List<A>>> map2 = list.stream().collect(
groupingBy(A::getName,groupingBy(a->{if(a.getAge()<18)return "child";
else return "adult";})));
判断每个分组有多少数量:
List<A> list = Arrays.asList(new A("tom",22),new A("tom",15),new A("jack",3));
Map<String, Long> map2 = list.stream().collect(
groupingBy(A::getName,counting()));
得到每个分组年龄最大的那个人:
List<A> list = Arrays.asList(new A("tom",22),new A("tom",15),new A("jack",3));
Map<String, Optional<A>> map2 = list.stream().collect(
groupingBy(A::getName,maxBy(comparing(A::getAge))));
Collectors.collectingAndThen 方法可以把结果转换为另一种类型。参数为collector和function。
List<A> list = Arrays.asList(new A("tom",22),new A("tom",15),new A("jack",3));
Map<String, A> map2 = list.stream().collect(
groupingBy(A::getName,collectingAndThen(maxBy(comparing(A::getAge)), Optional::get)));
mapping方法参数为function和Collector。
List<A> list = Arrays.asList(new A("tom",22),new A("tom",15),new A("jack",3));
Map<String, HashSet<Integer>> map2 = list.stream().collect(
groupingBy(A::getName,mapping(A::getAge, toCollection(HashSet::new))));
Collectors返回集合的方法有toList(),toSet(),toMap(),toConcurrentMap()方法。
3.分区
分区就是分组的特殊情况,分区的键值为boolean类型。参数为predicate。还有一个重载方法参数为predicate和Collector。
List<A> list = Arrays.asList(new A("tom",22),new A("tom",15),new A("jack",3));
Map<Boolean,List<A>> map2 = list.stream().collect(partitioningBy(a->a.getAge()>18));
分区的好处在于保留了true和false的两套列表。
4.Collector接口
Collector接口:
public interface Collector<T, A, R> {
Supplier<A> supplier();
BiConsumer<A, T> accumulator();
BinaryOperator<A> combiner();
Function<A, R> finisher();
Set<Characteristics> characteristics();
}
T为收集对象的泛型,A是累加器的类型,
累加器是在收集过程中用于累积部分结果的对象。 R是返回对象的类型。
supplier方法定义累加器类型。
accmulator方法把元素添加到累加器。
combiner方法合并两个结果容器。
finisher方法把累加器转为结果容器。
characteristics方法定义收集器行为,比如流是否可以并行归约,以及可以使用那些优化。
5.并行流
可以用parallelStream方法把集合转为并行流。并行流就是把内容分为几个数据块然后用不同的线程去执行这些数据块。这样就可以使用多核处理器的所有内核。
parallel()方法可以把流转为并行流。sequential()方法可以把并行流转为顺序流。
使用并行流的建议:
1.并行流不一定比顺序流快,需要检查流的性能。
2.最好不要装箱拆箱,可以的话使用IntStream这样的流。
3.有些操作并行流就会比顺序流慢,比如findFirst等等需要元素顺序的。
4.考虑流的操作在流水线的总计算成本 ,N为元素总数,Q为元素在流水线中的处理成本,Q越大用并行流的效率可能更高。
5. 小的数据量不要用并行流。
6.考虑流背后的数据结构是否易于分解。数组就比较好分解,链表就不好分解,range方法产生的流比较好分解,iterate不好分解。