java8源码_Java8-11-Stream收集器源码分析与自定义收集器

上一篇我们系统的学习了Stream的分组分区,本篇我们学习下Stream中的收集器。

那么什么是收集器呢,在之前的课程中,我们学习了可以通过Stream对集合中的元素进行例如映射,过滤,分组,分区等操作。例如下面将所有元素转成大写就是用map映射操作

List list = Arrays.asList("hello", "world", "helloworld");

List collect = list.stream().map(String::toUpperCase).collect(Collectors.toList());

现在再看上面的程序就很容易理解了,但是我们之前的文章只是对于中间操作(map方法等)进行了详细的介绍,包括lambda表达式和方法引用以及各种函数式接口。接下来我们将注意力放在collect方法上,collect接收一个Collector类型的参数,Collector就是Java8中的收集器。

R collect(Collector super T, A, R> collector);

也就是说collect方法最终需要接收一个收集器作为结果容器。虽然大多数收集器不需要我们自行创建,可以借助Collectors类提供的创建常用收集器的方法,例如toList() toSet() toCollection(Supplier collectionFactory)等方法。但是深入理解收集器的实现,对我们编写正确的程序会起到极大的作用。

下面就是toList方法的具体实现

public static Collector> toList() {

return new CollectorImpl<>((Supplier>) ArrayList::new, List::add,

(left, right) -> { left.addAll(right); return left; },

CH_ID);

}

通过查看toList方法源码,知道返回的收集器是一个CollectorImpl的实例。而CollectorImpl就是收集器Collector的一个实现类,被定义在Collectors辅助类中,用于创建常用的收集器实例供我们使用

/**

* Simple implementation class for {@code Collector}.

*

* @param the type of elements to be collected

* @param the type of the result

*/

static class CollectorImpl implements Collector {

private final Supplier supplier;

private final BiConsumer accumulator;

private final BinaryOperator combiner;

private final Function finisher;

private final Set characteristics;

CollectorImpl(Supplier supplier,

BiConsumer accumulator,

BinaryOperator combiner,

Function finisher,

Set characteristics) {

this.supplier = supplier;

this.accumulator = accumulator;

this.combiner = combiner;

this.finisher = finisher;

this.characteristics = characteristics;

}

CollectorImpl(Supplier supplier,

BiConsumer accumulator,

BinaryOperator combiner,

Set characteristics) {

this(supplier, accumulator, combiner, castingIdentity(), characteristics);

}

@Override

public BiConsumer accumulator() {

return accumulator;

}

@Override

public Supplier supplier() {

return supplier;

}

@Override

public BinaryOperator combiner() {

return combiner;

}

@Override

public Function finisher() {

return finisher;

}

@Override

public Set characteristics() {

return characteristics;

}

}

CollectorImpl构造方法根据传入的不同参数实现Collector接口中的方法,例如上面的toList

所以如果要实现自定义的收集器,就需要我们自己来实现Collector接口中的各个方法,接下来就接口中的每个方法进行分析

/*

* @param the type of input elements to the reduction operation

* @param the mutable accumulation type of the reduction operation (often

* hidden as an implementation detail)

* @param the result type of the reduction operation

* @since 1.8

*/

public interface Collector {

在分析Collector接口之前,我们需要关注下Collector接口的三个泛型

泛型T 表示向集合中放入的元素类型

泛型A 表示可变的中间结果容器类型

泛型R 表示最终的结果容器类型

下面我们还会提到这些泛型,接下来看下Collector接口中的方法

/**

* A function that creates and returns a new mutable result container.

*

* @return a function which returns a new, mutable result container

*/

Supplier supplier();

supplier()是一个创建并返回一个新的可变的结果容器的函数,也就是收集器工作时,首先要将收集的元素(也就是泛型T类型)放到supplier()创建的容器中。

/**

* A function that folds a value into a mutable result container.

*

* @return a function which folds a value into a mutable result container

*/

BiConsumer accumulator();

accumulator()是将一个个元素(泛型T类型)内容放到一个可变的结果容器(泛型A类型)中的函数,这个结果容器就是上面supplier()函数所创建的。

/**

* A function that accepts two partial results and merges them. The

* combiner function may fold state from one argument into the other and

* return that, or may return a new result container.

*

* @return a function which combines two partial results into a combined

* result

*/

BinaryOperator combiner();

combiner()会接收两部分结果容器(泛型A类型)并且将他们进行合并。即可以将一个结果集合并到另一个结果集中,也可以将这两个结果集合并到一个新的结果集中,并将得到的并集返回。

这里所说的结果集是指supplier()创建的结果容器中的所有元素,但是为什么说会接收两个结果集呢,这里涉及到并行流机制,如果是串行流执行只会生成一个结果容器不需要combiner()

函数进行合并,但是如果是并行流会生成多个结果容器,需要combiner()分别进行两两合并,最终得到一个最终的结果容器(泛型R类型)

其实并行流这里说的并不严谨,并行流需要结合Characteristics中的CONCURRENT特性值才能判断是否会产生多个中间可变结果容器,我们在后续分析收集器执行机制时,会结合示例来说明这部分的区别。

/**

* Perform the final transformation from the intermediate accumulation type

* {@code A} to the final result type {@code R}.

*

*

If the characteristic {@code IDENTITY_TRANSFORM} is

* set, this function may be presumed to be an identity transform with an

* unchecked cast from {@code A} to {@code R}.

*

* @return a function which transforms the intermediate result to the final

* result

*/

Function finisher();

finisher()会执行最终的转换操作,也就是说如果我们需要将得到的结果再次进行类型转换或者其他一些逻辑处理的话,可以通过finisher()完成。如果收集器包含了

Characteristics.IDENTITY_FINISH特性,说明不需要进行任何转换操作了,那么finisher()函数就不会执行。

/**

* Returns a {@code Set} of {@code Collector.Characteristics} indicating

* the characteristics of this Collector. This set should be immutable.

*

* @return an immutable set of collector characteristics

*/

Set characteristics();

最后来看下characteristics()函数,上面我们不止一次提到了收集器的特性值这个概念,characteristics()方法就是返回这些特性值的函数。这些特性值是我们创建收集器时,自己通过Characteristics指定的。Characteristics是一个定义在Collector接口中的枚举,它包括三个枚举值CONCURRENT,UNORDERED,IDENTITY_FINISH

/**

* Characteristics indicating properties of a {@code Collector}, which can

* be used to optimize reduction implementations.

*/

enum Characteristics {

/**

* Indicates that this collector is concurrent, meaning that

* the result container can support the accumulator function being

* called concurrently with the same result container from multiple

* threads.

*

*

If a {@code CONCURRENT} collector is not also {@code UNORDERED},

* then it should only be evaluated concurrently if applied to an

* unordered data source.

*/

CONCURRENT,

/**

* Indicates that the collection operation does not commit to preserving

* the encounter order of input elements. (This might be true if the

* result container has no intrinsic order, such as a {@link Set}.)

*/

UNORDERED,

/**

* Indicates that the finisher function is the identity function and

* can be elided. If set, it must be the case that an unchecked cast

* from A to R will succeed.

*/

IDENTITY_FINISH

}

如果包含了CONCURRENT特性值,表示这个收集器是支持并发操作的,这意味着多个线程可以同时调用accumulator()函数来向同一个中间结果容器放置元素。

注意这里是同一个中间结果容器而不是多个中间结果容器,也就是说如果包含了CONCURRENT特性值,(即使是并行流)只会产生一个中间结果容器,并且这个中间结果容器支持并发操作。

UNORDERED特性就很好理解了,它表示收集器中的元素是无序的。

IDENTITY_FINISH特性就表示确定得到的结果容器类型就是我们最终需要的类型,(在进行向最终类型强制类型转换时一定是成功的)

分析完我们总结一下:

1.supplier() 用于创建并返回一个可变的结果容器。

2.accumulator() 可以将元素累加到可变的结果容器中,也就是supplier()返回的容器。

3.combiner() 将两部分结果容器(也就是supplier()返回的容器)合并起来,可以是将一个结果容器合并到另一个结果容器中,也可以是将两个结果容器合并到一个新的空结果容器。

4.finisher() 执行最终的转换,将中间结果类型转换成最终的结果类型。

5.characteristics() 收集器的特性集合 不同的特性执行机制也不同

了解了Collector接口中的各个方法后,下面我们结合一个简单的需求,实现自己自的收集器

简单的需求就是将集合中的元素进行去重,这个需求十几枚多大意义,主要为了演示如何自定义收集器

public class MySetCollector implements Collector,Set>{

@Override

public Supplier> supplier() {

return HashSet::new;

}

@Override

public BiConsumer, T> accumulator() {

return Set::add;

}

@Override

public BinaryOperator> combiner() {

return (Set s1, Set s2) -> {

s1.addAll(s2);

return s1;

};

}

@Override

public Function, Set> finisher() {

return Function.identity();

}

@Override

public Set characteristics() {

EnumSet characteristicsEnumSet = EnumSet.of(Characteristics.UNORDERED,

Characteristics.IDENTITY_FINISH);//remove IDENTITY_FINISH finisher method will be invoked

return Collections.unmodifiableSet(characteristicsEnumSet);

}

public static void main(String[] args) {

List list = Arrays.asList("hello","world","welcome","hello");

Set collect = list.stream().collect(new MySetCollector());

System.out.println(collect);

}

}

MySetCollector类实现了Collector接口,并指定了三个泛型,集合中收集每个元素类型为T,中间结果容器类型为Set,不需要对中间结果容器类型进行转换,所以最终结果类型也是Set

supplier()中我们返回一个HashSet作为中间结果容器,accumulator()中调用Set的add方法将一个个元素加入到集合中,全都采用方法引用的方式实现。

然后combiner()对中间结果容器两两合并,finisher()中直接调用Function.identity()将合并后的中间结果容器作为最终的结果返回

/**

* Returns a function that always returns its input argument.

*

* @param the type of the input and output objects to the function

* @return a function that always returns its input argument

*/

static Function identity() {

return t -> t;

}

characteristics()方法定义了收集器的特性值,UNORDERED和IDENTITY_FINISH。表示容器中的元素是无序的并且不需要进行最终的类型转换

执行结果为[world, hello, welcome]

本篇我们通过分析收集器源码并结合一个简单的元素去重的需求实现了自己的收集器MySetCollector,下一篇我们会继续借助这个实例来分析收集器的执行机制。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值