Java 8 Stream API
- 通过 Stream.of 方法直接传入多个元素构成一个流
String[] arr = {“a”, “b”, “c”};
Stream.of(arr).forEach(System.out::println);
Stream.of(“a”, “b”, “c”).forEach(System.out::println);
Stream.of(1, 2, “a”).map(item -> item.getClass().getName())
.forEach(System.out::println);
- 通过 Stream.iterate 方法使用迭代的方式构造一个无限流,然后使用 limit 限制流元素个数
Stream.iterate(2, item -> item * 2).limit(10).forEach(System.out::println);
Stream.iterate(BigInteger.ZERO, n -> n.add(BigInteger.TEN))
.limit(10).forEach(System.out::println);
- 通过 Stream.generate 方法从外部传入一个提供元素的 Supplier 来构造无限流,再使用 limit 限制流元素个数
Stream.generate(() -> “test”).limit(3).forEach(System.out::println);
Stream.generate(Math::random).limit(10).forEach(System.out::println);
- 通过 IntStream 或 DoubleStream 构造基本类型的流
// IntStream 和 DoubleStream
IntStream.range(1, 3).forEach(System.out::println);
IntStream.range(0, 3).mapToObj(i -> “x”).forEach(System.out::println);
IntStream.rangeClosed(1, 3).forEach(System.out::println);
DoubleStream.of(1.1, 2.2, 3.3).forEach(System.out::println);
// 使用 Random 类创建随机流
new Random()
.ints(1, 100) // IntStream
.limit(10)
.forEach(System.out::println);
// 注意基本类型流和装箱后的流的区别
Arrays.asList(“a”, “b”, “c”).stream() // Stream
.mapToInt(String::length) // IntStream
.asLongStream() // LongStream
.mapToDouble(x -> x / 10.0) // DoubleStream
.boxed() // Stream
.mapToLong(x -> 1L) // LongStream
.mapToObj(x -> “”) // Stream
.collect(Collectors.toList());
中间操作
Stream 常用 API:

以下为测试用实体类,代码省略了 getter、settter 和构造方法
订单项
public class OrderItem {
private Long productId;// 商品ID
private String productName;// 商品名称
private Double productPrice;// 商品价格
private Integer productQuantity;// 商品数量
}
订单
public class Order {
private Long id;
private Long customerId;// 顾客ID
private String customerName;// 顾客姓名
private List orderItemList;// 订单商品明细
private Double totalPrice;// 总价格
private LocalDateTime placedAt;// 下单时间
}
消费者
public class Customer {
private Long id;
private String name;// 顾客姓名
}
filter
filter 操作用作过滤,类似于 SQL 中的 where 条件,接收一个 Predicate 谓词对象作为参数,返回过滤后的流
可以连续使用 filter 进行多层过滤
// 查找最近半年的金额大于40的订单
orders.stream()
.filter(Objects::nonNull) // 过滤null值
.filter(order -> order.getPlacedAt()
.isAfter(LocalDateTime.now().minusMonths(6))) // 最近半年的订单
.filter(order -> order.getTotalPrice() > 40) // 金额大于40的订单
.forEach(System.out::println);
map
map 操作用做转换,也叫投影,类似于 SQL 中的 select
// 计算所有订单商品数量
// 1. 通过两次遍历实现
LongAdder longAdder = new LongAdder();
orders.stream().forEach(order ->
order.getOrderItemList().forEach(orderItem -> longAdder.add(orderItem.getProductQuantity())));
System.out.println("longAdder = " + longAdder);
// 2. 使用两次 mapToLong 和 sum 方法实现
long sum = orders.stream().mapToLong(
order -> order.getOrderItemList().stream()
.mapToLong(OrderItem::getProductQuantity)
.sum()
).sum();
flatMap
flatMap 是扁平化操作,即先用 map 把每个元素替换为一个流,再展开这个流
// 统计所有订单的总价格
// 1. 直接展开订单商品进行价格统计
double sum1 = orders.stream()
.flatMap(order -> order.getOrderItemList().stream())
.mapToDouble(item -> item.getProductQuantity() * item.getProductPrice())
.sum();
// 2. 另一种方式 flatMapToDouble,即 flatMap + mapToDouble,返回 DoubleStream
double sum2 = orders.stream()
.flatMapToDouble(order ->
order.getOrderItemList()
.stream().mapToDouble(item ->
item.getProductQuantity() * item.getProductPrice())
)
.sum();
sorted
sorted 是排序操作,类似 SQL 中的 order by 子句,接收一个 Comparator 作为参数,可使用 Comparator.comparing 来由大到小排列,加上 reversed 表示倒叙
// 大于 50 的订单,按照订单价格倒序前 5
orders.stream()
.filter(order -> order.getTotalPrice() > 50)
.sorted(Comparator.comparing(Order::getTotalPrice).reversed())
.limit(5)
.forEach(System.out::println);
skip & limit
skip 用于跳过流中的项,limit 用于限制项的个数
// 按照下单时间排序,查询前 2 个订单的顾客姓名
orders.stream()
.sorted(Comparator.comparing(Order::getPlacedAt))
.map(order -> order.getCustomerName())
.limit(2)
.forEach(System.out::println);
// 按照下单时间排序,查询第 3 和第 4 个订单的顾客姓名
orders.stream()
.sorted(Comparator.comparing(Order::getPlacedAt))
.map(order -> order.getCustomerName())
.skip(2).limit(2)
.forEach(System.out::println);
distinct
distinct 操作的作用是去重,类似 SQL 中的 distinct
// 去重的下单用户
orders.stream()
.map(Order::getCustomerName)
.distinct()
.forEach(System.out::println);
// 所有购买过的商品
orders.stream()
.flatMap(order -> order.getOrderItemList().stream())
.map(OrderItem::getProductName)
.distinct()
.forEach(System.out::println);
终结操作
forEach
上文中已经多次使用,内部循环流中的所有元素,对每一个元素进行消费
forEachOrder 和 forEach 类似,但能保证消费顺序
count
返回流中项的个数
toArray
转换流为数组
anyMatch
短路操作,有一项匹配就返回 true
// 查询是否存在总价在 100 元以上的订单
boolean b = orders.stream()
.filter(order -> order.getTotalPrice() > 50)
.anyMatch(order -> order.getTotalPrice() > 100);
其他短路操作:
allMatch:全部匹配才返回 true
noneMatch:都不匹配才返回 true
findFirst:返回第一项的 Optional 包装
findAny:返回任意一项的 Optional 包装,串行流一般返回第一个
reduce
归纳,一边遍历,一边将处理结果保持起来,代入下一次循环
重载方法有三个,截图来自 IDEA 参数提示:

// 一个参数
// 求订单金额总价
Optional reduce = orders.stream()
.map(Order::getTotalPrice)
.reduce((p, n) -> {
return p + n;
});
// 两个参数
// 可指定一个初始值,初始值类型需要和 p、n 一致
Double reduce2 = orders.stream()
.map(Order::getTotalPrice)
.reduce(0.0, (p, n) -> {
return p + n;
});
三个参数的 reduce 方法:
可以接收一个目标结果类型的初始值,一个串行的处理函数,一个并行的合并函数
// 将所有订单的顾客名字进行拼接
// 第一个参数为目标结果类型,这里设置为空的 StringBuilder
// 第二个参数 BiFunction,参数为上一次的 StringBuilder 和流中的下一项,返回新的 StringBuilder
// 第三个参数 BinaryOperator,参数都是 StringBuilder,返回合并后的 StringBuilder
StringBuilder reduce = orders.stream()
.reduce(new StringBuilder(),
(sb, next) -> {
return sb.append(next.getCustomerName() + “,”);
},
(sb1, sb2) -> {
return sb1.append(sb2);
});
其他归纳方法:
max/min 求最大/最小值,接收一个比较器作为参数
对于 LongStream 等基础类型 Stream,则不需要传入比较器参数
collect
collect 是收集操作,对流进行终结操作,把流导出为我们需要的数据结构
collect 需要接收一个收集器 Collector 对象作为参数,JDK 内置的 Collector 的实现类 Collectors 包含了许多常用的导出方式
collect 常用 API:

导出流为集合:
- toList 和 toUnmodifiableList
// 转为 List(ArrayList)
orders.stream().collect(Collectors.toList());
// 转为不可修改的 List
orders.collect(Collectors.toUnmodifiableList());
- toSet 和 toUnmodifiableSet
// 转为 Set
orders.stream().collect(Collectors.toSet());
// 转为不可修改的 Set
orders.stream().collect(Collectors.toUnmodifiableSet());
- toCollection 指定集合类型,如 LinkedList
orders.stream().collect(Collectors.toCollection(LinkedList::new));
导出流为 Map:
- toMap,第三个参数可以指定键名重复时选择键值的规则
// 使用 toMap 获取订单 ID + 下单用户名的 Map
orders.stream()
.collect(Collectors.toMap(Order::getId, Order::getCustomerName))
.entrySet().forEach(System.out::println);
//使用 toMap 获取下单用户名 + 最近一次下单时间的 Map
orders.stream()
.collect(Collectors.toMap(Order::getCustomerName, Order::getPlacedAt,
(x, y) -> x.isAfter(y) ? x : y))
.entrySet().forEach(System.out::println);
-
toUnmodifiableMap:返回一个不可修改的 Map
-
toConcurrentMap:返回一个线程安全的 Map
分组导出:
在 toMap 中遇到重复的键名,通过指定一个处理函数来选择一个键值保留
大多数情况下,我们需要根据键名分组得到 Map,Collectors.groupingBy 是更好的选择
重载方法有三个,截图来自 IDEA 参数提示:

一个参数,等同于第二个参数为 Collectors.toList,即键值为 List 类型
// 按照下单用户名分组,键值是该顾客对应的订单 List
Map<String, List> collect = orders.stream()
.collect(Collectors.groupingBy(Order::getCustomerName));
两个参数,第二个参数用于指定键值类型
// 按照下单用户名分组,键值是该顾客对应的订单数量
Map<String, Long> collect = orders.stream()
.collect(Collectors.groupingBy(Order::getCustomerName,
Collectors.counting()));
三个参数,第二个参数用于指定分组结果的 Map 类型,第三个参数用于指定键值类型
// 按照下单用户名分组,键值是该顾客对应的所有商品的总价
Map<String, Double> collect = orders.stream()
.collect(Collectors.groupingBy(Order::getCustomerName,
Collectors.summingDouble(Order::getTotalPrice)));
// 指定分组结果的 Map 为 TreeMap 类型
Map<String, Double> collect = orders.stream()
.collect(Collectors.groupingBy(Order::getCustomerName,
TreeMap::new,
Collectors.summingDouble(Order::getTotalPrice)));
分区导出:
分区使用 Collectors.partitioningBy,就是将数据按照 TRUE 或者 FALSE 进行分组
// 按照是否有下单记录进行分区
customers.stream()
.collect(Collectors.partitioningBy(customer -> orders
.stream()
.mapToLong(Order::getCustomerId)
.anyMatch(id -> id == customer.getId())
));
// 等价于
customers.stream()
.collect(Collectors.partitioningBy(customer -> orders
.stream()
.filter(order -> order.getCustomerId() == customer.getId())
.findAny()
.isPresent()
));
类中间操作:
Collectors 还提供了类似于中间操作的 API,方便在收集时使用,如 counting、summingDouble、maxBy 等
Collectors.maxBy
// 获取下单量最多的商品,三种方式
// 使用收集器 maxBy
Map.Entry<String, Integer> e1 = orders.stream()
.flatMap(order -> order.getOrderItemList().stream())
.collect(Collectors.groupingBy(OrderItem::getProductName,
Collectors.summingInt(OrderItem::getProductQuantity)))
.entrySet()
.stream()
.collect(Collectors.maxBy(Map.Entry.<String, Integer>comparingByValue()))
.get();
// 使用中间操作 max
Map.Entry<String, Integer> e2 = orders.stream()
.flatMap(order -> order.getOrderItemList().stream())
.collect(Collectors.groupingBy(OrderItem::getProductName,
Collectors.summingInt(OrderItem::getProductQuantity)))
.entrySet()
.stream()
.max(Map.Entry.<String, Integer>comparingByValue())
.get();
// 由大到小排序,再 findFirst
Map.Entry<String, Integer> e3 = orders.stream()
.flatMap(order -> order.getOrderItemList().stream())
相关文章:
Java 8 Stream API
通过 Stream.of 方法直接传入多个元素构成一个流 String[] arr {“a”, “b”, “c”}; Stream.of(arr).forEach(System.out::println); Stream.of(“a”, “b”, “c”).forEach(System.out::println); Stream.of(1, 2, “a”).map(item -> item.getClass().getName()…...
亚博microros小车-原生ubuntu支持系列:21 颜色追踪
背景知识 这个测试例子用到了很多opencv的函数,举个例子。 #cv2.findContours函数来找到二值图像中的轮廓。#参数:#参数1:输 入的二值图像。通常是经过阈值处理后的图像,例如在颜色过滤之后生成的掩码。#参数2(cv2.RETR_EXTERNA…...
GESP6级语法知识(六):(动态规划算法(六)多重背包)
多重背包(二维数组) #include <iostream> using namespace std; #define N 1005 int Asd[N][N]; //Asd[i][j]表示前 i 个物品,背包容量是 j 的情况下的最大价值。 int Value[N], Vol[N], S[N];int main() {int n, Volume;cin &g…...
MySQL 事务实现原理( 详解 )
MySQL 主要是通过: 锁、Redo Log、Undo Log、MVCC来实现事务 事务的隔离性利用锁机制实现 原子性、一致性和持久性由事务的 redo 日志和undo 日志来保证。 Redo Log(重做日志):记录事务对数据库的所有修改,在崩溃时恢复未提交的更改,保证事务…...
AI协助探索AI新构型自动化创新的技术实现
一、AI自进化架构的核心范式 1. 元代码生成与模块化重构 - 代码级自编程:基于神经架构搜索的强化学习框架,AI可通过生成元代码模板(框架的抽象层定义)自动组合功能模块。例如,使用注意力机制作为原子单元ÿ…...
九. Redis 持久化-RDB(详细讲解说明,一个配置一个说明分析,步步讲解到位)
九. Redis 持久化-RDB(详细讲解说明,一个配置一个说明分析,步步讲解到位) 文章目录 九. Redis 持久化-RDB(详细讲解说明,一个配置一个说明分析,步步讲解到位)1. RDB 概述2. RDB 持久化执行流程3. RDB 的详细配置4. RDB 备份&恢…...
mac连接linux服务器
1、mac连接linux服务器 # ssh -p 22 root192.168.1.152、mac指定密码连接linux服务器 (1) 先安装sshpass,下载后解压执行 ./configure && make && makeinstall https://sourceforge.net/projects/sshpass/ (2) 连接linux # sshpass -p \/\\\[\!\\wen12\$ s…...
oracle: 表分区>>范围分区,列表分区,散列分区/哈希分区,间隔分区,参考分区,组合分区,子分区/复合分区/组合分区
分区表 是将一个逻辑上的大表按照特定的规则划分为多个物理上的子表,这些子表称为分区。 分区可以基于不同的维度,如时间、数值范围、字符串值等,将数据分散存储在不同的分区 中,以提高数据管理的效率和查询性能,同时…...
使用Pygame制作“走迷宫”游戏
1. 前言 迷宫游戏是最经典的 2D 游戏类型之一:在一个由墙壁和通道构成的地图里,玩家需要绕过障碍、寻找通路,最终抵达出口。它不但简单易实现,又兼具可玩性,还能在此基础上添加怪物、道具、机关等元素。本篇文章将展示…...
AJAX案例——图片上传个人信息操作
黑马程序员视频地址: AJAX-Day02-11.图片上传https://www.bilibili.com/video/BV1MN411y7pw?vd_source0a2d366696f87e241adc64419bf12cab&spm_id_from333.788.videopod.episodes&p26 图片上传 <!-- 文件选择元素 --><input type"file"…...
Day35-【13003】短文,什么是双端队列?栈和队列的互相模拟,以及解决队列模拟栈时出栈时间开销大的方法
文章目录 第三节进一步讨论栈和队列双端队列栈和队列的相互模拟使用栈来模拟队列类型定义入队出队判空,判满 使用队列来模拟栈类型定义初始化清空操作判空,判满栈长度输出入栈出栈避免出栈时间开销大的方法 第三节进一步讨论栈和队列 双端队列 假设你芷…...
力扣 55. 跳跃游戏
🔗 https://leetcode.cn/problems/jump-game 题目 给一个数组 nums,最开始在 index 0,每次可以跳跃的区间是 0-nums[i]判断是否可以跳到数组末尾 思路 题解是用贪心,实际上模拟也可以过遍历可以到达的下标,判断其可…...
深入剖析 HTML5 新特性:语义化标签和表单控件完全指南
系列文章目录 01-从零开始学 HTML:构建网页的基本框架与技巧 02-HTML常见文本标签解析:从基础到进阶的全面指南 03-HTML从入门到精通:链接与图像标签全解析 04-HTML 列表标签全解析:无序与有序列表的深度应用 05-HTML表格标签全面…...
本地快速部署DeepSeek-R1模型——2025新年贺岁
一晃年初六了,春节长假余额马上归零了。今天下午在我的电脑上成功部署了DeepSeek-R1模型,抽个时间和大家简单分享一下过程: 概述 DeepSeek模型 是一家由中国知名量化私募巨头幻方量化创立的人工智能公司,致力于开发高效、高性能…...
MVC 文件夹:架构之美与实际应用
MVC 文件夹:架构之美与实际应用 引言 MVC(Model-View-Controller)是一种设计模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种架构模式不仅提高了代码的可维护性和可扩展性,而且使得开发流程更加清晰。本文将深入探讨MVC文…...
Redis --- 秒杀优化方案(阻塞队列+基于Stream流的消息队列)
下面是我们的秒杀流程: 对于正常的秒杀处理,我们需要多次查询数据库,会给数据库造成相当大的压力,这个时候我们需要加入缓存,进而缓解数据库压力。 在上面的图示中,我们可以将一条流水线的任务拆成两条流水…...
如何确认设备文件 /dev/fb0 对应的帧缓冲设备是开发板上的LCD屏?如何查看LCD屏的属性信息?
要判断 /dev/fb0 是否对应的是 LCD 屏幕,可以通过以下几种方法: 方法 1:使用 fbset 命令查看帧缓冲设备的属性信息 Linux 的 帧缓冲设备(Framebuffer) 通常在 /dev/fbX 下,/dev/fb0 一般是主屏幕ÿ…...
C++多线程编程——基于策略模式、单例模式和简单工厂模式的可扩展智能析构线程
1. thread对象的析构问题 在 C 多线程标准库中,创建 thread 对象后,必须在对象析构前决定是 detach 还是 join。若在 thread 对象销毁时仍未做出决策,程序将会终止。 然而,在创建 thread 对象后、调用 join 前的代码中ÿ…...
AI与SEO关键词的完美结合如何提升网站流量与排名策略
内容概要 在当今数字营销环境中,内容的成功不仅依赖于高质量的创作,还包括高效的关键词策略。AI与SEO关键词的结合,正是这一趋势的重要体现。 AI技术在SEO中的重要性 在数字营销领域,AI技术的引入为SEO策略带来了前所未有的变革。…...
保姆级教程Docker部署Kafka官方镜像
目录 一、安装Docker及可视化工具 二、单节点部署 1、创建挂载目录 2、运行Kafka容器 3、Compose运行Kafka容器 4、查看Kafka运行状态 三、集群部署 在Kafka2.8版本之前,Kafka是强依赖于Zookeeper中间件的,这本身就很不合理,中间件依赖…...
利用最小二乘法找圆心和半径
#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...
C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...
Objective-C常用命名规范总结
【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名(Class Name)2.协议名(Protocol Name)3.方法名(Method Name)4.属性名(Property Name)5.局部变量/实例变量(Local / Instance Variables&…...
【大模型RAG】Docker 一键部署 Milvus 完整攻略
本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装;只需暴露 19530(gRPC)与 9091(HTTP/WebUI)两个端口,即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...
第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明
AI 领域的快速发展正在催生一个新时代,智能代理(agents)不再是孤立的个体,而是能够像一个数字团队一样协作。然而,当前 AI 生态系统的碎片化阻碍了这一愿景的实现,导致了“AI 巴别塔问题”——不同代理之间…...
USB Over IP专用硬件的5个特点
USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中,从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备(如专用硬件设备),从而消除了直接物理连接的需要。USB over IP的…...
脑机新手指南(七):OpenBCI_GUI:从环境搭建到数据可视化(上)
一、OpenBCI_GUI 项目概述 (一)项目背景与目标 OpenBCI 是一个开源的脑电信号采集硬件平台,其配套的 OpenBCI_GUI 则是专为该硬件设计的图形化界面工具。对于研究人员、开发者和学生而言,首次接触 OpenBCI 设备时,往…...
Python 训练营打卡 Day 47
注意力热力图可视化 在day 46代码的基础上,对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...
CppCon 2015 学习:REFLECTION TECHNIQUES IN C++
关于 Reflection(反射) 这个概念,总结一下: Reflection(反射)是什么? 反射是对类型的自我检查能力(Introspection) 可以查看类的成员变量、成员函数等信息。反射允许枚…...
虚幻基础:角色旋转
能帮到你的话,就给个赞吧 😘 文章目录 移动组件使用控制器所需旋转:组件 使用 控制器旋转将旋转朝向运动:组件 使用 移动方向旋转 控制器旋转和移动旋转 缺点移动旋转:必须移动才能旋转,不移动不旋转控制器…...
