Java8 Stream实现原理,源码浅析(上)

2023年 10月 14日 78.7k 0

Stream🚩

Stream提供了对Java集合的一种全新的操作方式,由于简约的风格,以及不错的性能,已经成为Java开发人员必须掌握的基本技能。下面直观地感受一下Stream流的用法:

Stream快速开始

// 排序
numbers = numbers.stream().sorted(Integer::compareTo).collect(Collectors.toList());
// 根据ID去重
List likeTidList = likeDOs.stream().map(LikeDO::getTid)
                .distinct().collect(Collectors.toList());

前置知识:lambda表达式概述

由于Stream依赖lambda表达式,因此先做一些铺垫:lambda表达式为何能作为方法的入参?要点在于:函数式接口。函数式编程是对行为抽象

函数式接口

定义:只有一个抽象方法的接口。

即使接口还包含其它默认方法default和静态方法static,只要抽象方法只有一个,就行

比如最常见的:Runnable

// 函数式接口注解
@FunctionalInterface
public interface Runnable {
 public abstract void run();
}

lambda表达式

lambda表达式是对函数式接口的实现。在没有lambda的时代,我们往往会用匿名函数,实现接口的方法。但是这样做代码量大,复杂。lambda表达式的特点就是极其轻量化,能简则简。

Runnable runnable = () -> {  //代码更加简洁
    System.out.println("Hello world!");
};

lambda的原理

关键点在于对字节码的分析,可以参考:
Java Lambda表达式原理解析

总之,Lambda表达式会被编译生成一个私有的静态函数,并且生成一个实现了函数式接口的内部类。在这个内部类的唯一的抽象方法中,会调用生成的私有的静态函数来实现该唯一的抽象方法。

下面正式开始介绍Stream的实现原理。流的原理基本可以划分为三个部分:流的创建,中间操作,以及终止操作,我们逐个来看。

一、Stream的创建

list.stream()

这是获取一个流对象,但我们要知道,流对象到底是什么?有什么核心字段?如何存储数据源?有多少种方式创建流对象?

Stream的创建方式

1、Stream.of(T... values)

public static Stream of(T... values) {
    return Arrays.stream(values);
}
// 本质是Arrays.stream,实质就是:
return StreamSupport.stream(spliterator(array, startInclusive, endExclusive), false);
// 又是StreamSupport.stream

2、Collection.stream()

也就是所有Collection的子类都可以,包括ArrayList,ArrayDeque

default Stream stream() {
    return StreamSupport.stream(spliterator(), false);
}

3、Arrays.stream(T [])

数组当然也支持,前文也说过本质还是StreamSupport.stream

StreamSupport:真正创建Stream对象

来看看它的stream方法

// StreamSupport.stream
    public static  Stream stream(Spliterator spliterator, boolean parallel) {
        Objects.requireNonNull(spliterator);
        return new ReferencePipeline.Head        (spliterator,StreamOpFlag.fromCharacteristics(spliterator),parallel);
}

可以看到,返回的实际上是ReferencePipeline的内部类Head(实现了Stream接口) 。

并且传递了一个参数Spliterator。那么这两个类就是分析的重点

Spliterator:数据源的载体

Spliterator的中文翻译过来是分离器,但是这并不能很好的帮助你理解Spliterator。

Spliterator最大的意义是:替Stream存储数据源,处理数据源,辅助并行操作。

这里以数组为例,ArraySpliterator来分析:(各种Spliterator实例都被封装在Spliterators类的内部类中)

1、存数源数据

// 数组获取spliterator的方法
   return Spliterators.spliterator(array, startInclusive, endExclusive,...);
// Spliterators.spliterator方法
public static  Spliterator spliterator(Object[] array, int fromIndex, int toIndex,
                                             int additionalCharacteristics) {
    return new ArraySpliterator(array, fromIndex, toIndex, additionalCharacteristics);
}
static final class ArraySpliterator implements Spliterator{
        private final Object[] array;
        private int index;        // current index, modified on advance/split
        private final int fence;  // one past last index
        private final int characteristics;
}

这就可以看到Spliterator的一个作用:存数源数据。

2、处理数据的能力

然后再来看看它的核心方法:

// lambda表达式,消费一个元素
public boolean tryAdvance(Consumer source,
                 int sourceFlags, boolean parallel) {
    this.previousStage = null;
    // 可以看到 用 source 命名 Spliterator
    // 而在下面两个的构造方法是没有赋值sourceSpliterator的
    this.sourceSpliterator = source;
    this.sourceStage = this;
    this.sourceOrOpFlags = sourceFlags & StreamOpFlag.STREAM_MASK;
    this.combinedFlags = (~(sourceOrOpFlags  upstream) {
        return new OfRef(upstream);
    }

发现,返回的是个OfRef,而OfRef正是StatefulOp的一个子类

private static final class OfRef extends ReferencePipeline.StatefulOp {
   // isNaturalSort表示元素本身是否可以排序(传参数不传比较器则为true,否则为false)
   private final boolean isNaturalSort;
   private final Comparator

相关文章

JavaScript2024新功能:Object.groupBy、正则表达式v标志
PHP trim 函数对多字节字符的使用和限制
新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
为React 19做准备:WordPress 6.6用户指南
如何删除WordPress中的所有评论

发布评论