disgare 的博客
首页
博客
分类
标签
首页
博客
分类
标签
  • 网络

    • 计算机网络学习笔记
    • 网络安全相关
    • 域名和子网掩码
    • CORS 跨域资源共享
    • DNS、HTTP 与 HTTPS
    • Server-Sent Events (SSE)
    • WebSocket 长连接
  • 计算机基础

    • 操作系统 IO 相关知识
    • 操作系统学习笔记
    • 程序的机器级表示
    • 音频文件基础
    • 正则表达式相关概念
    • ffmpeg 的安装以及实现音频切分功能
    • Hex 和 Base64 编码
    • XML 的使用
  • 数据结构与算法

    • 动态规划算法学习笔记
    • 基于比较的排序算法的最坏情况下的最优下界为什么是O(nlogn)
    • 集合与数据结构学习笔记
    • 面试常见算法总结
    • 算法导论第二部分排序学习笔记
    • 算法导论第一部分学习笔记
  • Java

    • 对象之间的映射与转换
    • 反射学习笔记
    • 泛型相关概念
    • 关于 boolean 类型的坑
    • 如何使用 lambda 表达式实现排序
    • CompletableFuture 相关用法
    • CompletableFuture 源码浅要阅读
    • FutureTask 源码阅读
    • Guava 常用 API
    • Guava 源码阅读:Multimap 相关
    • Jackson 的各种使用
    • Java 的 Excel 相关操作
    • java 的常见性能问题分析以及出现场景
    • java 基础知识
    • JAVA 枚举的基础和原理
    • Java 图片文件上传下载处理
    • Java 序列化
    • Java 异常
    • Java 语法糖
    • Java 中关于字符串处理的常用方法
    • Java 中强、软、弱、虚引用
    • JAVA 注解小结
    • Java Http 访问框架
    • Java Stream 的使用
      • 开始
      • 中间操作
        • forEach 遍历
        • map 映射
        • flatMap 平铺
        • filter 过滤
        • limit 限制
        • sorted 排序
        • distinct 去重
        • boxed 包装
        • peek
      • 结束操作
        • collect 收集
        • toList、toSet 和 toMap
        • Collectors.groupingBy
        • Collectors.mapping
        • Collectors.collectingAndThen
        • metch 匹配
        • find 查询
        • findFirst 与 findAny 的使用
        • Optional 的获取
        • count 计数
        • reduce 规约
      • 原理
    • Java8 新特性
    • netty 学习笔记
    • Scanner 的各种用法
    • Servlet 学习笔记
    • String、StringBuffer、StringBuilder 学习笔记
  • JVM

    • 虚拟机执行子系统
    • JVM 自动内存管理
    • Linux 中 JVM 常用工具以及常见问题解决思路
  • Linux

    • crontab 表达式
    • Linux 常见命令
    • Linux 文件系统
  • 中间件

    • 关于定时任务原理
    • 详解 kafka
    • ES 搜索引擎
    • flink 提交流程
    • Grape-RAG
    • Hadoop 基础原理
  • 多线程

    • 多线程基础学习笔记
    • 简单了解并发集合
    • 如何手写单例
    • 深入理解 java 多线程安全
    • 生产者消费者问题
    • 线程池作用、用法以及原理
    • AQS 组件
    • ThreadLocal 原理以及使用
  • 非关系型数据库

    • Redis 集群
    • Redis 数据结构、对象与数据库
    • Redis 学习笔记
  • 关系型数据库

    • B+ 树的插入、删除和数据页分裂机制
    • MySQL 的 binglog、redolog、undolog
    • MySQL 的记录存储结构、存储引擎与 Buffer Pool
    • MySQL 基本的特性
    • MySQL 开发规范
    • MySQL 事务与锁与 MVCC
    • MySQL 数据类型、字符集相关内容
    • MySQL 索引与索引优化
    • PostgreSQL 更新数据时 HOT优化
    • PostgreSQL 相关用法
  • Python

    • Python 基础语法
    • Python 学习
  • Spring 项目

    • Lombok 的常用注解
    • maven 小结
    • MyBatis 框架的使用
    • MyBatis 重要知识点总结
    • MybatisPlus 的使用
    • Spring 框架基础使用
    • Spring 事务相关
    • Spring IOC 的原理及源码
    • Spring AOP 的使用和原理
    • SpringBoot 的原理
    • SpringBoot 基础使用
    • SpringWeb 重要知识点
  • 分布式

    • 初步了解 docker
    • 从 ACID 到 BASE 事务处理的实现
    • 访问远程服务
    • 分布式 id
    • 分布式缓存相关问题
    • 分布式集群理论和分布式事务协议
    • 分布式架构的观测
    • 分布式一致性算法
    • 负载均衡 Load Balancing
    • 关于分布式系统 RPC 中高可用功能的实现
    • 集群间数据同步的目的
    • 三高问题下的系统优化
    • 数据库分库分表
    • 详解 Spring Cloud
    • Dubbo 基础概念
    • Gossip 协议
    • nginx 学习笔记
    • Protobuf 通信协议
    • Zookeeper 基础学习
  • 架构设计

    • 参数校验与异常处理
    • 抽象方法与设计模式
    • 代码整洁之道
    • 权限系统设计
    • 用低内存处理大量数据
    • 设计模式——策略模式
    • 设计模式——过滤器模式在 Spring 中的实践
    • 状态模式
    • 统一结果返回
    • 为什么要打日志?怎么打日志?打什么日志?
    • 运维监控常见指标含义
    • 资深研发进阶
    • DDD 架构学习笔记
    • Java 常用的规则引擎
    • MVC 架构学习笔记
  • AI

    • 如何编写 Prompt
    • Agent 工程架构
    • LLM 相关内容
    • NLP 相关知识
    • vibe coding 最佳实践
    • windows 下 ollama 迁移到 D 盘
  • 开发工具

    • 如何画时序图、流程图、状态流转图
    • excel 关于 =vlookup 的用法
    • git 的学习以及使用
    • IDEA 插件推荐
    • IDEA 常用快捷键以及调试
    • Shell 脚本
    • swagger 的使用
  • 前端

    • 简单了解前端页面开发
    • 伪静态是什么
    • GitHub Pages 部署教程
    • Vercel 部署教程
    • vue-admin-template 简单使用
    • VuePress 博客搭建指南
  • 项目

    • 面试刷题网——技术方案
    • 影视资源聚合站——技术方案
  • 问题记录

    • 定时任务单线程消费 redis 中数据导致消费能力不足
    • 提供可传递的易受攻击的依赖项
    • Liteflow 在 SpringBoot 启动时无法注入组件问题 couldn‘t find chain with the id[THEN(NodeComponent)]
  • 金融

    • 股票分析——关于电力
    • 股票技术面——量价关系
    • 股票技术面——盘口
    • 股票技术面——基础
    • 基础的金融知识
    • 基金与股票
    • 韭菜的自我总结
    • 聊聊价值投资
  • 其他

    • 程序员职场工作需要注意什么
    • 创业全链路SOP:从灵光一现到系统化增长的实战指南
    • 观罗翔讲刑法随笔
    • 价格和价值
    • 立直麻将牌效益理论
    • 梅花易数学习笔记
    • 压力管理
2023-11-14
Java
目录

Java Stream 的使用

Stream 的创建需要指定一个数据源,比如 java.util.Collection 的子类,任何集合类对象以及数组都可以作为这个数据源

它的作用就是链式的对一组元素进行操作,它的操作分为中间操作或者最终操作两种,最终操作返回一特定类型的计算结果,而中间操作返回 Stream 本身,以下是常见的操作 在这里插入图片描述 无状态:指元素的处理不受之前元素的影响 有状态:指该操作只有拿到所有元素之后才能继续下去 非短路操作:指必须处理所有元素才能得到最终结果 短路操作:指遇到某些符合条件的元素就可以得到最终结果

Stream 可以使用串行和并行来完成操作,串行操作是用一个线程依次执行,而并行操作使用了多线程,将 stream() 改为 parallelStream() 来使用并行流

流只能用计数器计数:流虽然可以实现很多高大上的操作,但是遇到需要使用数组下标的问题还是只能使用老土的计数器方式,因此如果程序中涉及到要取下标的操作还是推荐使用 for 循环

    int index = 0;
    list.stream().filter(s -> 
            //每比对一个元素,数值加1
            s.getId() == 10086 ? true : index++ == -1;
        ).findFirst();
1
2
3
4
5

流的操作大多需要使用 lambda 重写逻辑,它的常见操作如下:

# 开始

我们对一个集合或者迭代器使用 stream 方法就可以得到一个流了

   		Lists.newArrayList().stream();
   		Lists.newArrayList().parallelStream();
1
2

可以使用 java.util.Arrays.stream(T[] array) 方法用数组创建流

		int[] array={1,3,5,6,8};
		IntStream stream = Arrays.stream(array);
1
2

还可以使用 Stream 的 of 方法获取一个或者多个流

		Stream.of(splitter.splitToList(a, b));
1

同时,在 of 之后,使用 flatMap 方法即可将多个集合转换为一个流。使用 of 方法的话,各个数组并不是分别映射一个流,而是映射成多个流

		Stream.of(splitter.splitToList(a, b)).flatMap(Arrays::stream);
1

此时使用 flatMap(Array::stream) 可以将生成的多个流被合并起来,即扁平化为一个流

# 中间操作

# forEach 遍历

该方法是最常见的,该方法用来迭代流中的每个数据,不只是 stream 对象,集合也可以直接使用 forEach 方法

		integers.forEach(System.out::println);
1

# map 映射

该方法用于映射每个元素到对应的结果

		integers.stream().map(i -> i+1).forEach(System.out::println);
1

# flatMap 平铺

map 是对流元素进行转换,flatMap 是对流中的元素(数组)进行平铺后合并,即对流中的每个元素平铺后又转换成为了 Stream 流。flatMap 必须返回一个 stream 流,他会将每次返回的流中数据连起来

System.out.println("=====flatmap list=====");
List<String> mapList = list.stream().flatMap(Arrays::stream).collect(Collectors.toList());
mapList.forEach(System.out::print);
System.out.println("\nflatmap list size: " + mapList.size());
System.out.println();
1
2
3
4
5

如果是数组的话一般在里面直接填入 Arrays::stream 即可,如果是 list 的话传入 Collection::stream

# filter 过滤

该方法用于通过设置的条件过滤出元素,只有满足条件的(里面的方法返回为 true)才会留下来,其他的都会被过滤掉。该方法不会删除原集合的数据

		integers.stream().filter(a -> a/2 == 0).forEach(System.out::println);
1

# limit 限制

该方法用于获取指定数量的流。 以下代码片段使用 limit 方法打印出前 10 条数据

	integers.limit(10).forEach(System.out::println);
1

# sorted 排序

该方法用于对流用指定的操作进行排序,如果不传入 Comparator 排序器,则默认使用自然排序,自然排序需要流中元素需实现 Comparable 接口

	integers.sort((a1, a2) -> a2 - a1).forEach(System.out::println);
1

# distinct 去重

方法用于去除流中的重复元素,该方法不用传入参数

	integers.distinct().forEach(System.out::println);
1

distinct 使用 hashCode 和 equals 方法来获取不同的元素。因此,我们的类必须实现 hashCode 和 equals 方法

# boxed 包装

java 中有很多未经包装的基本数据类型形成的流,比如 IntStream、LongStream、DoubleStream,而 Collectors.toList() 等收集器只能处理对象流(如 Stream<Integer>),不能直接处理 IntStream。因此,需要使用 boxed() 将 int 转换为 Integer

# peek

是一个中间操作,它接受一个 Consumer 函数式接口,对流中的每个元素执行指定的操作,同时返回一个包含相同元素的新流

一般用于打印调试信息

List<String> result = list.stream()
    .filter(s -> s.length() > 3)
    .peek(s -> System.out.println("After filter: " + s))
    .map(String::toUpperCase)
    .peek(s -> System.out.println("After map: " + s))
    .collect(Collectors.toList());
1
2
3
4
5
6

下面是危险操作,因为某些优化会忽略 peek,因此像是修改对象内容的逻辑使用 peek 的话可能不会执行

class Person {
    private String name;
    
    public void setName(String name) { this.name = name; }
    public String getName() { return name; }
}

List<Person> people = Arrays.asList(new Person(), new Person());

List<Person> updated = people.stream()
    .peek(p -> p.setName("Unknown"))  // 修改对象状态
    .collect(Collectors.toList());
1
2
3
4
5
6
7
8
9
10
11
12

# 结束操作

以上方法属于中间操作,返回 Stream 本身,结束操作不会指的是不返回 Stream 的方法

# collect 收集

该方法可以返回列表或者字符串,该方法可以接收一个集合实例,将流中元素收集成该集合实例

但是 collect 的功能不止于此,它可以说是内容最繁多、功能最丰富的部分了。从字面上去理解,就是把一个流收集起来,最终可以是收集成一个值也可以收集成一个新的集合

# toList、toSet 和 toMap

collect 主要依赖 java.util.stream.Collectors 类内置的静态方法,下面用一个案例演示 toList、toSet 和 toMap,以及把一个集合配合一些分隔符链接为一个字符串

List<String> list = strings.stream().collect(Collectors.toList());

List<String> set = strings.stream().collect(Collectors.toList());

Map<String, String> map = strings.stream().collect(Collectors.toMap(k -> k, v -> v));

String mergedString = strings.stream().collect(Collectors.joining(", "));
1
2
3
4
5
6
7

我们在 Collectors.toMap 中对重复的 key 去重

Map<Integer, String> map = list.stream().collect(Collectors.toMap(Person::getId, Person::getName, (oldValue, newValue) -> newValue));
1

Function.identity() 这个函数代表输入什么值就输出什么值,相当与 v -> v

后面的参数则代表遇到重复的 key 时在老值与新值之中取哪个值,举个例子:

list.stream().collect(Collectors.toMap(Person::getId, Function.identity(), (oldV, newV) -> oldV)).values().stream();
1

你还可以根据这个特性将重复的 key,放入 List,(当然我们也可以通过 groupBy 完成这件事)

Map<String, List<Working>> map =
                workings.stream().collect(Collectors.toMap(Working::getInvoicePage,
                        e -> {
                            ArrayList<Working> list = new ArrayList<>();
                            list.add(e);
                            return list;
                        },
                        (oldList, newList) -> {
                            oldList.addAll(newList);
                            return oldList;
                        }));
1
2
3
4
5
6
7
8
9
10
11

# Collectors.groupingBy

举个例子,我们可以优雅的对某个集合做分组统计,比如在学生这个 pojo 中,对属性班级做分组或者做分组统计

Map<Integer, List<Student>> studentGroup = studentList.stream().collect(Collectors.groupingBy(Student::getClassNumber));

Map<Integer, Long> map = studentList.stream().collect(Collectors.groupingBy(Student::getClassNumber, Collectors.counting()));
1
2
3

上面例子中的第二个,我们修改了返回 Map 值的类型。第二个重载 groupingBy 方法带另一个参数指定后续收集器,应用于第一个集合结果。当我们仅指定一个分类器函数,没有后续收集器,则返回 toList() 集合。如何后续收集器使用 toSet(),则会获得 Set 集合,而不是 List。我们甚至可以对已经分组的集合再进行分组

Map<BlogPostType, Set<BlogPost>> postsPerType = posts.stream()
  .collect(groupingBy(BlogPost::getType, Collectors.toSet()));

Map<String, Map<BlogPostType, List>> map = posts.stream()
  .collect(groupingBy(BlogPost::getAuthor, Collectors.groupingBy(BlogPost::getType)));
1
2
3
4
5

来个常用的功能:收集 pojo 中一对多关系 map

        Map<Long, Set<Long>> parentAuthorMap = list.stream().collect(Collectors.groupingBy(AccountRelation::getParentId,
                Collectors.mapping(AccountRelation::getAuthorId, Collectors.toSet())));
1
2

三个参数的 groupingBy 方法,通过提供 Map supplier 函数,其允许我们改变 Map 的类型

EnumMap<BlogPostType, List<BlogPost>> postsPerType = posts.stream()
  .collect(groupingBy(BlogPost::getType, 
  () -> new EnumMap<>(BlogPostType.class), Collectors.toList()));
1
2
3

# Collectors.mapping

groupingBy 方法的第二个入参,可以是 mapping 方法,这个方法是用来干啥的呢?

基本语法:

Collector<T, ?, R> mapping(Function<? super T, ? extends U> mapper, Collector<? super U, A, R> downstream)
1
  • mapper:一个函数,用于将流中的每个元素转换为另一个对象
  • downstream:一个收集器,用于处理和收集转换后的元素

eg:假设我们有一个 List,我们想将每个字符串转换为大写,并收集到一个新的列表中

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

        List<String> upperCaseNames = names.stream()
            .collect(Collectors.mapping(String::toUpperCase, Collectors.toList()));

        System.out.println(upperCaseNames); // 输出: [ALICE, BOB, CHARLIE]
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

在这个例子中:

String::toUpperCase 是一个函数,它将每个字符串转换为大写 Collectors.toList() 是一个下游收集器,它将转换后的元素收集到一个列表中

# Collectors.collectingAndThen

Collectors.collectingAndThen() 函数应该最像 map and reduce 了,它可接受两个参数,第一个参数用于 reduce 操作,而第二参数用于 map 操作

先把流中的所有元素传递给第一个参数,然后把生成的集合传递给第二个参数来处理

例如下面的代码

  • 把 [1,2,3,4] 这个集合传递给 v -> v * 2 lambda表达式,计算得出结果为[2,4,6,8]
  • 然后再把 [2,4,6,8]传递给 Collectors.averagingLong 表达式,计算得出 5.0
  • 然后传递给 s -> s * s lambda表达式,计算得到结果为 25.0
@Test
public void collectingAndThenExample() {
    List<Integer> list = Arrays.asList(1, 2, 3, 4);
    Double result = list.stream().collect(Collectors.collectingAndThen(Collectors.averagingLong(v -> {
                System.out.println("v--" + v + "--> " + v * 2);
                return v * 2;
            }),
            s -> {
                System.out.println("s--" + s + "--> " + s * s);
                return s * s;
            }));
    System.out.println(result);
}
1
2
3
4
5
6
7
8
9
10
11
12
13

再比如,我想用对象中的某个属性 list 去重,就可以这么写

                Stream.of(wechatUserInfosFromBackend, wechatUserInfosFromC2b)
                        .flatMap(Collection::stream)
                        .collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<WechatUserInfo>(Comparator.comparing(WechatUserInfo::getExternalUserid))), ArrayList::new))
1
2
3

# metch 匹配

metch 函数只返回 true 与 false,该方法会对传入的数据进行逐个判断,有以下几种类型

  • allMatch:接收一个 Predicate 函数,当流中每个元素都符合该断言时才返回 true,否则返回 false
  • noneMatch:接收一个 Predicate 函数,当流中每个元素都不符合该断言时才返回 true,否则返回 false
  • anyMatch:接收一个 Predicate 函数,只要流中有一个元素满足该断言则返回 true,否则返回 false

# find 查询

有以下两个分支

  • findFirst 返回流中满足条件的第一个元素
  • findAny:返回流中找到的第一个元素

这两个方法在串行流中的概念以及效果是一模一样的,不同之处在于并行流

# findFirst 与 findAny 的使用

在并行流中的 findAny() 操作,返回的元素是不确定的,对于同一个列表多次调用 findAny() 有可能会返回不同的值。使用 findAny() 是为了更高效的性能。如果是数据较少,串行地情况下,一般会返回第一个结果,如果是并行的情况,那就不能确保是第一个

举个栗子:

        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        System.out.println(list.parallelStream().filter(a -> a.equals(2) || a.equals(1) || a.equals(3)).findAny().get());
        System.out.println(list.parallelStream().filter(a -> a.equals(2) || a.equals(1) || a.equals(3)).findFirst().get());
1
2
3
4
5
6

这里两个并行流的结果是不一样的,findAny 方法会侧重第一个查到返回值的线程,而 findFirst 则会侧重数组中第一个满足条件的值

# Optional 的获取

Optional 是对 stream 使用 findFirst 或者 findAny 方法会得到的类,它是为了防止空指针问题而被创造出来的,本来这块内容不应该是流结束操作涉及到的,但是 findAny 或者 findAny 也算是和 stream 结束有关系

我们在获取 Optional 后直接 get 会提示没有进行赋值检查,因此不推荐直接 get,准确的写法如下:

     Optional.of("has value").orElse(getDefault());
     Optional.of("has value").orElseGet(() -> getDefault());
     Optional.of("has value").ifPresent(A::setB);
1
2
3

说一下 orElse 与 orElseGet 的区别,orElse 是传值的,所以里面的表达式会立即执行(在传入一个方法的时候),如果 optional 有值也会执行就没必要了;而 orElseGet 接受的是一个 function,只有 optional 为空的时候才会被执行,因此不会让 cpu 资源被浪费

尽量在 orElse 中传入属性,在 orElseGet 中传入方法,如果在 orElse 中传入了方法,而且方法中含有更新修改类的操作,这样就不光是 CPU 或者耗时的问题了

# count 计数

返回流中元素的总个数

	strings.stream().count();
1

# reduce 规约

规约操作(reduction operation)又被称作折叠操作(fold),是通过某个连接动作将所有元素汇总成一个汇总结果的过程。元素求和、求最大值或最小值、求出元素总个数、合并、将所有元素转换成一个元素,都属于规约操作

reduce 擅长的是生成一个值,而 collect 擅长从 Stream 中生成一个集合或者 Map 等复杂的对象

reduce 的方法定义有三种重写形式,我们需要按顺序定义以下三种模式:

  • Identity : 定义一个元素代表是归并操作的初始值,如果Stream 是空的,也是 Stream 的默认结果
  • Accumulator: 定义一个带两个参数的函数,第一个参数是上个归并函数的返回值,第二个是Strem 中下一个元素
  • Combiner: 调用一个函数来组合归并操作的结果,当归并是并行执行或者当累加器的函数和累加器的实现类型不匹配时才会调用此函数。因为 reduce 操作默认是返回一个与流中数据同类型的值,比如如果流中是字符串只能合并成一个字符串,如果我们想返回整形,那就需要用到 Combiner 了

举几个例子,我们可以用它找出数组中最有特色的值,比如最大值最小值:

    Optional<String> res = Stream.of("zhangsan", "lisi", "wanger", "mazi")
            .reduce((s1, s2) -> s1.length() >= s2.length() ? s1 : s2);
    System.out.println(res.get()); 

    Optional<String> res2 = Stream.of("zhangsan", "lisi", "wanger", "mazi")
            .max((s1, s2) -> s1.length() - s2.length());
    System.out.println(res2.get()); 
1
2
3
4
5
6
7

可以用它来求和

        Integer res1 = Arrays.stream(new Integer[]{2, 4, 6, 8})
                .reduce(0, Integer::sum);
1
2

很明显我们只能将列表变成列表中的一个元素,那假如需要将整数列表变成一个字符串,我们该如何操作呢?我们当然可以先使用 map 做转换,但是也可以使用 reduce 提供的第三个参数

int result = users.stream()
  .reduce("", (a, b) -> a + String.valueOf(b), String::concat);
1
2

# 原理

简单聊一下 stream 的原理,stream 是一个基于源、零个或多个中间操作、一个终止操作构建的、支持惰性求值和短路优化的函数式数据处理管道

整个 Stream 操作分为两步,构建流水线(filter、map、sorted 等方法,此时会将这些操作记录(链接)起来,形成一个执行计划,而不会立即进行任何数据处理)和触发执行(调用终止操作时会启动,此时数据开始从头到尾依次通过每个操作节点)

JDK 对于 stream 的实现非常巧妙,它使用了双向链表,核心的概念是 stage 和 sink

1,Stage(阶段):流水线上的每一个操作(包括源和终止操作)在内部都被抽象为一个 Stage。这些 Stage 用双向链表连接起来,每个 Stage 都知道它的上一个(源)和下一个(下游)阶段 2,Sink(槽):每个 Stage 内部都包含一个 Sink 对象的链。Sink 是一个核心接口,定义了数据处理的方法:

  • begin(long size): 开始前调用,可选
  • end(): 结束后调用,可选
  • accept(T t): 最重要的方法! 处理一个元素
  • cancellationRequested(): 是否可以提前结束(用于短路操作)

当调用终止操作时,会先从流水线的最后(终止操作) 开始,向前遍历每个 Stage,将每个 Stage 的操作逻辑包装成对应的 Sink。然后将这些槽按顺序连接起来,形成一条 Sink 链。最后一个 Sink 通常负责产生结果。从源头(第一个 Stage)开始,逐个取出数据元素

对于每个数据,调用第一个 Sink 的 accept() 方法。在这个 accept() 方法里,会执行当前 Stage 的操作(如过滤),然后判断是否需要将处理后的结果传递给下游 Sink 的 accept() 方法。如此一环扣一环,数据就会被推过整个 Sink 链

stream 做了一些优化,比如会使用并行流工作窃取的方式(Work Stealing)来平衡线程负载。同时在无状态的操作放在有状态

#Stream
最后更新: 2/23/2026, 9:23:04 AM
Java Http 访问框架
Java8 新特性

← Java Http 访问框架 Java8 新特性→

最近更新
01
vibe coding 最佳实践
02-24
02
立直麻将牌效益理论
02-23
03
伪静态是什么
02-08
更多文章>
Theme by Vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式