通过使用Stream,我们可以对集合进行流式计算,最简单的使用如下:
List<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
list.add("every");
list.add("day");
list.stream().forEach(s -> System.out.println(s));
复制代码
首先看一下Stream的描述
A sequence of elements supporting sequential and parallel aggregate operations
复制代码
翻译过来就是,支持对一序列元素进行串行和并行的聚合操作
Stream继承BaseStream
public interface BaseStream<T, S extends BaseStream<T, S>>
extends AutoCloseable {
/**
* 访问元素的迭代器
*/
Iterator<T> iterator();
/**
* 分割器,对元素进行分割后并行访问
*/
Spliterator<T> spliterator();
/**
* 判断Stream是否是并行
*/
boolean isParallel();
/**
* 返回串行Stream或自己
*/
S sequential();
/**
* 返回并行Stream或者自己
*/
S parallel();
S unordered();
S onClose(Runnable closeHandler);
void close();
}
复制代码
stream()是Collection接口的dafault方法
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
复制代码
首先看一下spliterator()方法
default Spliterator<E> spliterator() {
return Spliterators.spliterator(this, 0);
}
复制代码
Spliterators为分割器提供创建分割器实例和操作方法,spliterator便是其中一个。spliterator有两个参数,第一个就是集合自身,也就是说,Spliterator持有集合实例
然后在看StreamSupport的stream方法
public static <T> Stream<T> stream(Spliterator<T> spliterator, boolean parallel) {
Objects.requireNonNull(spliterator);
return new ReferencePipeline.Head<>(spliterator,
StreamOpFlag.fromCharacteristics(spliterator),
parallel);
}
复制代码
Head是ReferencePipeline的静态内部类,在Head的构造器里,会把Spliterator作为第一个参数传进去。ReferencePipeline是流式计算的提供者
ReferencePipeline提供了所有的流式操作,我们主要看一下forEach方法
public void forEach(Consumer<? super P_OUT> action) {
evaluate(ForEachOps.makeRef(action, false));
}
复制代码
forEach就一个函数接口,就是我们例子里的lamda表达式s -> System.out.println(s)
public static <T> TerminalOp<T, Void> makeRef(Consumer<? super T> action,
boolean ordered) {
Objects.requireNonNull(action);
return new ForEachOp.OfRef<>(action, ordered);
}
复制代码
OfRef其实也是Consumer,Sink也是
所以最终还是会调用到Comsumer的accept方法
现在还有一个问题,就是怎样可以让集合的 每个元素都调用到accept方法
ForEachOp
public <S> Void evaluateSequential(PipelineHelper<T> helper,
Spliterator<S> spliterator) {
return helper.wrapAndCopyInto(this, spliterator).get();
}
复制代码
这个方法,会把自己包装成一个Sink,Sink翻译过来就是算子,简单理解成一个执行计算的任务,最后的get方法等待结果返回,但ForEachOp本身没有结果返回,所以这里返回的是null
继续进去wrapAndCopyInto,会进入到ArrayList的静态内部类ArrayListSpliterator的forEachRemaining方法
public void forEachRemaining(Consumer<? super E> action) {
int i, hi, mc; // hoist accesses and checks from loop
ArrayList<E> lst; Object[] a;
if (action == null)
throw new NullPointerException();
if ((lst = list) != null && (a = lst.elementData) != null) {
if ((hi = fence) < 0) {
mc = lst.modCount;
hi = lst.size;
}
else
mc = expectedModCount;
if ((i = index) >= 0 && (index = hi) <= a.length) {
for (; i < hi; ++i) {
@SuppressWarnings("unchecked") E e = (E) a[i];
action.accept(e);
}
if (lst.modCount == mc)
return;
}
}
throw new ConcurrentModificationException();
}
复制代码
很明显,最后执行for循环,遍历元素,执行accept方法,对应的就是上面所说的lamda表达式