前一小结对Collector接口进行了总结介绍,并没有对其实现进行展开,接下来对Collectors进行展开,Collections本身提供了关于Collectors的常见汇聚实现,Collectors其实就是一个工厂。collector由四个函数指定,这些函数一起工作,将条目累积到可变结果容器中,并可以选择对结果执行最终转换,将会在Collectors看到Collector的实习内幕。本节主要分析层次设计,不对细节方法进行深入分析,Collectors工厂类将以两小节进行分析,当我们看清楚Collectors工厂类内部设计的时候,可能对于源码的理解会更好。
// 将姓名累加到列表中
* List<String> list = people.stream()
* .map(Person::getName)
* .collect(Collectors.toList());
*
* // 把名字累加成树集
* Set<String> set1 =people.stream()
* .map(Person::getName)
* .collect(Collectors.toCollection(TreeSet::new));
*
* // 将元素转换为字符串并将它们连接起来,用逗号分隔
* String joined = things.stream()
* .map(Object::toString)
* .collect(Collectors.joining(", "));
*
* // 计算职工工资总额
* int total = employees.stream()
* .collect(Collectors.summingInt(Employee::getSalary)));
*
* // 按部门分组员工
* Map<Department, List<Employee>> byDept
* = employees.stream()
* .collect(Collectors.groupingBy(Employee::getDepartment));
*
* // 按部门计算工资总额
* Map<Department, Integer> totalByDept
* = employees.stream()
* .collect(Collectors.groupingBy(Employee::getDepartment,
* Collectors.summingInt(Employee::getSalary)));
*
* //把学生分为及格和不及格
* Map<Boolean, List<Student>> passingFailing =
* students.stream()
* .collect(Collectors.partitioningBy(s -> s.getGrade()>=PASS_THRESHOLD));
*
以上是对Collectors简单介绍,定义加示例方式;
抑制默认构造函数,确保不可实例化。
private Collectors() {
}
前面已经有讲过在SztreamAPI源码分析之一对Characteristics的特性进行了分析,这里就做太多讲解了。
static final Set<Collector.Characteristics> CH_CONCURRENT_ID
= Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.CONCURRENT,
Collector.Characteristics.UNORDERED,
Collector.Characteristics.IDENTITY_FINISH));
static final Set<Collector.Characteristics> CH_CONCURRENT_NOID
= Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.CONCURRENT,
Collector.Characteristics.UNORDERED));
static final Set<Collector.Characteristics> CH_ID
= Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH));
static final Set<Collector.Characteristics> CH_UNORDERED_ID
= Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.UNORDERED,
Collector.Characteristics.IDENTITY_FINISH));
static final Set<Collector.Characteristics> CH_NOID = Collections.emptySet();
首先可以看出引用集合变量使用static +final修饰,static就不多介绍了在这里,因为这将是一个工厂类,对外将会是static方法提供,所以需要静态变量,final修饰符在这这里就是把初始化出来的变量不在被重新引用其他的指针,默认protected,本包下java.util.stream,所以注定了内部使用的集合容器,在加上Collections.unmodifiableSet生成不可变集合,保证线程安全。
这些变量集合将在接下来的Collectors工厂类发挥极具重要的作用。
CollectorImpl是Collector真正的实现类,但是可能好多人肯定会有一个疑问,为什么不直接由Collectors实现那?这样子是不是多此一举那?
这里要从Java的特性开始讲起,Java有三大特性:继承、多态、封装。在我们普通的业务中,可能继承就是为了快速完成任务,上线交工,但是JDK的开发工作可不是一照这个流程进行的,而是想着1.8版本之后,这个类如何在伸缩性、拓展性、功能增强行考虑,当然还有性能。
那这个时候一个补全Java单继承短板的设计出现了,那就是嵌套类,即被定义在另一个类的内部的类。嵌套类存在的目的只是为了它的外围类提供服务。今天只有一个CollectorImpl内部类,可能下个版本就会有另一个内部提供另一种功能服务,而且与原有的功能不冲突,增加新内部类以及新方法,很好的避开了维护成本。这里设计成静态内部类是因为这是一个Collectors工厂类,方法都是静态方法,需要静态的变量,而这个时候,其实静态内部类只能当静态变量使用(可能有些描述不合适,可以这样先理解吧,如果有大佬知道,望指教下,但是确实是需要静态内部类)
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;
CollectorImpl(Supplier<A> supplier,
BiConsumer<A, T> accumulator,
BinaryOperator<A> combiner,
Function<A,R> finisher,
Set<Characteristics> characteristics) {
this.supplier = supplier;
this.accumulator = accumulator;
this.combiner = combiner;
this.finisher = finisher;
this.characteristics = characteristics;
}
CollectorImpl(Supplier<A> supplier,
BiConsumer<A, T> accumulator,
BinaryOperator<A> combiner,
Set<Characteristics> characteristics) {
this(supplier, accumulator, combiner, castingIdentity(), characteristics);
}
@Override
public BiConsumer<A, T> accumulator() {
return accumulator;
}
@Override
public Supplier<A> supplier() {
return supplier;
}
@Override
public BinaryOperator<A> combiner() {
return combiner;
}
@Override
public Function<A, R> finisher() {
return finisher;
}
@Override
public Set<Characteristics> characteristics() {
return characteristics;
}
}
CollectorImpl的五个变量
两个四参五参的构造函数不进行解释了。之后就是Collector的五个方法进行了实现,基本都是返回工厂类的实现方式,放入是什么实现,就使用什么实现,也不做过多讲解了,没啥难度的。
一个私有静态常量内部类,首先这个类私有化就已经确定是为这个Collectors工厂类服务了,就是因为工厂类所以需要静态类来提供,常量类是因为需要保证这个类生成以后是一份,也而且这个类需要的情况比较单一,提供的服务比较单一,就是为Collectors工厂类一个分组方法提供服务;之后我们也看到 extends AbstractMap
private static final class Partition<T>
extends AbstractMap<Boolean, T>
implements Map<Boolean, T> {
final T forTrue;
final T forFalse;
Partition(T forTrue, T forFalse) {
this.forTrue = forTrue;
this.forFalse = forFalse;
}
@Override
public Set<Map.Entry<Boolean, T>> entrySet() {
return new AbstractSet<Map.Entry<Boolean, T>>() {
@Override
public Iterator<Map.Entry<Boolean, T>> iterator() {
Map.Entry<Boolean, T> falseEntry = new SimpleImmutableEntry<>(false, forFalse);
Map.Entry<Boolean, T> trueEntry = new SimpleImmutableEntry<>(true, forTrue);
return Arrays.asList(falseEntry, trueEntry).iterator();
}
@Override
public int size() {
return 2;
}
};
}
}
两个变量:
构造方法:双参
重写AbstractMap
首先方法中对AbstractSet抽象类的父类AbstractCollection的迭代器方法进行实现,
实现细节分析:
Map.Entry<Boolean, T> falseEntry = new SimpleImmutableEntry<>(false, forFalse);
Map.Entry<Boolean, T> trueEntry = new SimpleImmutableEntry<>(true, forTrue);
return Arrays.asList(falseEntry, trueEntry).iterator();
首先是生成两个新的Entry
之后对AbstractCollection的size()方法进行实现,原本AbstractSet集合的大小本来就是true集合和false集合,所以这里是定值2;