Java Stream基本原理

·  阅读 56

通过使用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是流式计算的提供者

Image.png

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也是

Image.png

所以最终还是会调用到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表达式

分类:
后端
标签:
收藏成功!
已添加到「」, 点击更改