list.stream().map(i -> i * 2).collect(Collectors.toList);安全
Collector是由四个功能指定一块儿工做以累加条目到一个可变的结果容器,和任选地执行该结果的最终变换:并发
建立一个新的结果容器: supplier()
app
结合有新的数据元素到结果容器: accumulator()
jvm
accumulator()
一次两个结果的容器组合成一个: combiner()
ide
combiner的逻辑:假设有1,2,3,4号线程处理,返回了4个结果,有可能出现的组合方式是:函数
finisher()
Characteristics
是一个枚举类,来判断结果集合容器是否支持并行操做。为了保证并发安全,保证并行和串行执行的结果一致,收集器函数必须知足两个条件this
同一性(identity):线程
a == combiner.apply(a, supplier.get())
结合性(associativity):分割计算必须获得一个等价的结果code
UNORDERED
,则只须要两个结果集中的内容是相同的,无视顺序。实现基于汇聚操做的Collector ,如Stream.collect(Collector) ,必须遵循如下限制:对象
accumulator()
的第一个参数,以及传递给combiner()
的两个参数,并传递给finisher()
的参数(这3种其实都是结果容器的类型),每一次的结果容器类型必须和上一次的结果容器类型相同。accumulator()
、combiner()
、finisher()
的结果相关的任何操做。finisher()
,相同的对象没有从函数返回的(说明你使用了新的结果容器,jdk认为以前的结果容器已经被使用完成了),它永远不会再使用。combiner()
或finisher()
,说明accumulator()
已经被执行完成了,则不会再调用它。supplier()
、accumulator()
、combiner()
的返回必须串行线程限制,保证不会有其余线程可以获取。 经过线程封闭来实现,Collector就不须要实现任何额外的同步。 减小执行必须管理输入正确分区,该分区在隔离处理,并在combine
完成后,才执行finsher
操做。accumulator()
。而且仅当UNORDERED
时,才应该使用这种并发缩减。(为了线程安全,此时的容器应该是线程安全的)收集器自己是支持compose(组合)
,例如计算出员工工资总和的一个收集器,能够被经过分组的组合再次使用。
Collector<Employee, ?, Integer> summingSalaries
= Collectors.summingInt(Employee::getSalary))
Collector<Employee, ?, Map<Department, Integer>> summingSalariesByDept
= Collectors.groupingBy(Employee::getDepartment, summingSalaries);
static class CollectorImpl<T, A, R> implements Collector<T, A, R> {
private final Supplier<A> supplier;
private final BiConsumer<A, T> accumulator;
private final BinaryOperator<A> combiner;
private final Function<A, R> finisher;
private final Set<Characteristics> characteristics;
default Comparator<T> reversed() {
return Collections.reverseOrder(this);
}
Student student1 = new Student("A", 90);
Student student2 = new Student("B", 80);
Student student3 = new Student("C", 100);
Student student4 = new Student("D", 90);
Student student5 = new Student("D", 70);
List<Student> list = Arrays.asList(student1, student2, student3, student4, student5);
Map<String, Student> map2 = list.stream().collect(Collectors.groupingBy(Student::getName,
Collectors.collectingAndThen(Collectors.minBy(Comparator.comparingInt(Student::getScore)), Optional::get)));
System.out.println(map2);
public class MySetCollector<T> implements Collector<T, Set<T>, Set<T>> {
@Override
public Supplier<Set<T>> supplier() {
System.out.println("supplier invoked");
return HashSet::new;
}
@Override
public BiConsumer<Set<T>, T> accumulator() {
System.out.println("accumulator invoked");
return Set::add;
}
@Override
public BinaryOperator<Set<T>> combiner() {
System.out.println("combiner invoked");
return (set1, set2) -> {
set1.addAll(set2);
return set1;
};
}
@Override
public Function<Set<T>, Set<T>> finisher() {
System.out.println("finisher invoked");
return set -> set;
}
@Override
public Set<Characteristics> characteristics() {
System.out.println("characteristics invoked");
return Collections.unmodifiableSet(EnumSet.of(Characteristics.IDENTITY_FINISH, Characteristics.UNORDERED));
}
}
supplier
:构造中间结果容器accmulator
:将流中的元素累加到中间结果容器中combiner
:将fork的中间结果容器join起来finisher
:若是中间结果容器和须要返回的结果容器不一样,须要finisher
内实现转化代码characteristics
:定义当前收集器的特性
CONCURRENT
:中间结果容器线程安全,支持并发写入。若是设置了CONCURRENT,收集器将放弃forkjoin模式,直接让线程操做同一个中间结果容器。
CONCURRENT
属性,若是出现安全性问题(即并发修改),jvm将会抛出ConcurrentModificationException
。实现自定义收集器,必须override上面这5个方法: 自定义收集器实现 -------- 源码实现上应该是当comparingInt的返回结果为0时,才会继续调用thenComparing 先根据name排序,若是name相等,则根据分数排序。 thenCoparing能够理解为次级排序 ### thenComparing 默认比较器为升序排序,若是调用reversed,则返回的为倒序排序 ### reversed Comparator是一个函数式接口,只有一个抽象方法`int compare(T o1, T o2);`,其他提供了不少默认方法。 * 返回-1,则表示o1比o2小。 * 返回 0,则表示o1和o2相等 * 返回1,则表示o1比o2大 Comparator是一个比较器,从JDK1.2时候开始提供。其中最重要的方法应该是`int compare(T o1, T o2);`方法。 Comparator ---------- Collectors是一个收集器工厂,为开发者提供一些经常使用的收集器和收集器方法。Collectors的收集器是私有的,因此没法直接实例化这个工厂。 `Collectors`是`Collector`接口的官方惟一实现类。`Collecotrs`维护了一个CollectorImpl的内部类 Collectors ---------- * `CONCURRENT`:表示这个收集器是能够并发的。 * 这里的并发和parallelStream不一样,parallStream是经过建立多个结果容器,再经过combiner合并到一块儿。 * 而这个Concurrent表示,同一个结果容器支持多个线程同时调用。(这个容器须要线程安全的) * `UNORDERED`:收集操做并不保证与输入元素顺序一致。 * `IDENTITY_FINISH`:若是中间的结果容器类型就是返回容器的类型,这时候才能够配置,finsher函数会直接将结果类型转化为传入的参数类型 #### Characteristics