1.方法引用
-
类名::静态方法名
-
引用名(对象名)::实例方法名
-
类名::实例方法名
-
构造方法引用
2.stream
流由️三部分组成
流操作的分类
Collection提供了新的Stream()方法
-
流不存储值,通过管道的方式获取值
-
本质是函数式的,对流的操作会生成一个结果,不过并不会修改底层的数据源,集合可以做为流的底层数据源
-
延迟查找,很多流操作(过滤,映射,排序)都可以实现延迟操作
集合和流的区别
-
集合关注的是数据与数据存储本身,流关注的是则是对数据的计算.
-
流与迭代器类似的一点: 流是无法重复使用和消费的
3.收集器 Collect o<T,A,R>
T 代表流中元素的类型
A 代表中间操作收集容器的类型
R 代表返回的结果类型
Supplier<A> supplier()
BiConsumer<A, T> accumulator()
-
累加器,不断的从流中遍历元素,然后将T累加到A当中
BinaryOperator<A> combiner()
-
并发的,将多个线程所执行的部分结果合并到一起,将后面的合并到前面,其实是返回第一个(具体看下面源码分析)
Function<A, R> finisher()
-
可选的方法, 完成器. 将不断累积的结果A转换为结果R(其实不难理解,A就是最后的结果R)
Set<Characteristics> characteristics()
4.枚举类Characteristics
CONCURRENT
-
如果一个收集器是CONCURRENT的话,中间结果容器只会有唯一的一个,多个线程会同时操作这个结果容器;如果不加这个参数,那么如果有多少个线程,就会产生多少个中间结果容器,最后进行一个combiner的合并.
UNORDERED
IDENTITY_FINISH
-
加了这个参数不会调用 finisher()方法, 会讲中间结果进行强制类型转换,转换为结果的R类型.
PS:收集器其实就是一个可变的容器,我们可以随便操作
5.Collectors源码实现
Collectors静态工厂类,其中实现有两种情况:
-
通过CollectorImpl来实现
-
通过reducing方法来实现(本质也是CollectorImpl) ‘
1.Collectors.toList()方法,下面附源码,其实很简单
public static <T>
Collector<T, ?, List<T>> toList() {
return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,
(left, right) -> { left.addAll(right); return left; },
CH_ID);
}
这个是Collectors.toCollection()方法,必须传入参数 supplier(),用于指定中间返回类型和结果返回的集合类型,而Collectors.toList()在实现中已经指定,所以不需要传递参数
public static <T, C extends Collection<T>>
Collector<T, ?, C> toCollection(Supplier<C> collectionFactory) {
return new CollectorImpl<>(collectionFactory, Collection<T>::add,
(r1, r2) -> { r1.addAll(r2); return r1; },
CH_ID);
}
public static <T, U, A, R>
Collector<T, ?, R> mapping(Function<? super T, ? extends U> mapper,
Collector<? super U, A, R> downstream) {
BiConsumer<A, ? super U> downstreamAccumulator = downstream.accumulator();
return new CollectorImpl<>(downstream.supplier(),
(r, t) -> downstreamAccumulator.accept(r, mapper.apply(t)),
downstream.combiner(), downstream.finisher(),
downstream.characteristics());
}
Mapping()在应用到多级汇聚的情况下最有用,例如:
Map<City, Set<String>> lastNamesByCity
= people.stream().collect(groupingBy(Person::getCity, mapping(Person::getLastName, toSet())));
public static<T,A,R,RR> Collector<T,A,RR> collectingAndThen(Collector<T,A,R> downstream,
Function<R,RR> finisher) {
Set<Collector.Characteristics> characteristics = downstream.characteristics();
if (characteristics.contains(Collector.Characteristics.IDENTITY_FINISH)) {
if (characteristics.size() == 1)
characteristics = Collectors.CH_NOID;
else {
characteristics = EnumSet.copyOf(characteristics);
characteristics.remove(Collector.Characteristics.IDENTITY_FINISH);
characteristics = Collections.unmodifiableSet(characteristics);
}
}
return new CollectorImpl<>(downstream.supplier(),
downstream.accumulator(),
downstream.combiner(),
downstream.finisher().andThen(finisher),
characteristics);
}
collectingAndThen() 通过适配器对上次的结果再做一次转换,转换为一个不可变的集合,源码中的示例:
* <pre>{@code
* List<String> people
* = people.stream().collect(collectingAndThen(toList(), Collections::unmodifiableList));
* }</pre>
解读: 源码中通过if判断,其实本质上是把IDENTITY_FINISH去掉,通过上面的分析篇幅知道他的作用是不会调用 finisher()函数,会把结果进行强制转换,转换成一个最终的返回结果; 但是对于collectingAndThen()函数来说,必须要执行参数传递进来的这个finisher()函数.
public static <T> Collector<T, ?, Integer>
summingInt(ToIntFunction<? super T> mapper) {
return new CollectorImpl<>(
() -> new int[1],
(a, t) -> { a[0] += mapper.applyAsInt(t); },
(a, b) -> { a[0] += b[0]; return a; },
a -> a[0], CH_NOID);
}
summingInt() 函数的实现源码中先会创建一个supplier, 为啥是创建一个长度为1的() -> new int[1]的数组.,而不是直接创建一个数字?
因为数组是引用类型;数字是值类型,数字没法传递,所以不会能传递到后续的方法中进行累加,就只是一个固定的值;最重要的是搜集器必须是一个容器.
public static <T> Collector<T, ?, Double>
averagingInt(ToIntFunction<? super T> mapper) {
return new CollectorImpl<>(
() -> new long[2],
(a, t) -> { a[0] += mapper.applyAsInt(t); a[1]++; },
(a, b) -> { a[0] += b[0]; a[1] += b[1]; return a; },
a -> (a[1] == 0) ? 0.0d : (double) a[0] / a[1], CH_NOID);
}
averagingInt 这个函数的作用是求平均值,所以在创建收集器的时候会创建一个长度为2的数组容器,数组中第一个值就是累加所以值的总和,第二个值就是有几个数, 在源码中可以看出来,当在多线程的情况下,各自进行累加.
public static <T, K> Collector<T, ?, Map<K, List<T>>>
groupingBy(Function<? super T, ? extends K> classifier) {
return groupingBy(classifier, toList());
}
<T, K> T代表输入的元素类型
<T, K> K代表返回结果Map中的key的类型