Java8的Stream最佳实践
从这一篇文章开始,我们会由浅入深,全面的学习stream API的最佳实践(结合我的使用经验),本想一篇写完,但写着写着发现需要写的内容太多了,所以分成一个系列慢慢来说。给大家分享我的经验的同时,也促使我复习每一个细节,大家共同进步。
Stream是什么
Java 8新增了一个API叫做Stream ,Stream的英文可以理解为流动的液体,可能很多人一听脑子里的第一印象就是流式计算,不自觉地就心生畏惧,感觉非常的高深莫测。其实这就是一个辅助处理集合数据的工具类,工具的更新必然带来的是生产力的提升,这里的生产力代表的就是整洁优雅的代码,更高的灵活度,更好的性能。相信各类的技术文章(包括博客和书籍)已经写过无数遍了。比如下面摘录《Java 8实战》关于流的描述:
流是Java API的新成员,它允许你以声明性方式处理数据集合(通过查询语句来表达,而不是临时编写一个实现)。就现在来说,你可以把它们看成遍历数据集的高级迭代器。此外,流还可以透明地并行处理,你无需写任何多线程代码了!
这段话的表述个人感觉类似于抓手、赋能、心智之类的PPT黑话,看着挺高级的,也能懂一些,但也不是很懂,反正如果对于不知道Stream的人,并不能建立直接的理解。
所以流到底是什么呢?是一个接口。让我们看看它的声明:
public interface Stream<T> extends BaseStream<T, Stream<T>> {Stream<T> filter(Predicate<? super T> predicate);<R> Stream<R> map(Function<? super T, ? extends R> mapper);void forEach(Consumer<? super T> action);...
}
就是个接口,然后这个借口有一些抽象方法:filter,map,forEach等等。我们可以看到有些方法返回了新的Stream,有些直接是void。这个接口用来干什么用呢?处理集合数据。为什么这么说?看下面一个Collection接口的方法:
public interface Collection<E> extends Iterable<E> {...default Stream<E> stream() {return StreamSupport.stream(spliterator(), false);}
}
那么所有继承了Collection的接口都可以直接创建Stream,然后再执行Stream里面的操作。所以这么看下来,首先得承认书中的表述是高度抽象且精炼的,这是书籍该做的事情。但从易于理解的角度,我觉得可以说是简洁高效安全的处理集合数据的工具类。如下图所示,Stream是一个中间过程。

需要注意的点
- 首先Stream不是一个数据结构,它不存储任何数据,它是一种数据处理工具,代表了一种能力。
- Stream不会对处理的数据本身做任何修改,永远都是返回新的Stream或者最终的处理结果。
- Stream可以有多个中间操作,但只能有一个终端操作,因为终端操作就求值了。
- 一个Stream只能用一次,不能多次复用。(因为它不存储数据,只是一个转换能力)。
能力范围
Stream随着Java 8的发布已经8年多了,在我有限的职业生涯里,碰到的一些职场新人依然有些人觉得使用for或者iterator来遍历集合更易读易懂。但如果他真正了解Stream所蕴含的能力后,应该会转变想法。下面简单介绍一下Stream都提供了什么样的能力。
1. 生成流
- java.util.stream.Stream#of(T… values) 。首先stream接口本身提供了一个静态默认方法,可以直接创建,这里的可变参数会被解析成一个数组。
- java.util.Collection#stream()
- java.util.Arrays#stream(T[] array)
- java.nio.file.Files#list(Path dir)
- java.nio.file.Files#lines(Path path)
可以看到,可以操作stream的对象基本为List或者Array.
2. 筛选和切片
这可能是用的最多的功能。对应的方法为:
- filter:接受一个Predicate断言函数,用来遍历元素是否符合断言条件。可以简单的理解为一个过滤器。
- distinct:无参数,将所有元素去重,和数据库的distinct关键词能力一样。
- limit:接受一个int型长度字段,表示要保留多少个元素,需要注意的时候limit并不排序。
- skip:和limit相对应,接受一个int型长度字段表示跳过多少个元素,也不排序。
下面举个例子:
Stream.of("d2", "a2", "b1", "a3", "c1", "a4", "a2", "a1").filter(x -> x.startsWith("a")).distinct().skip(1).limit(3).forEach(System.out::println);}
// output:
a3
a4
a1
3. 映射/转换
这里主要是map,map代表了一种对应关系,即地图坐标与实际地点的对应关系,我们有了经纬度就可以准确的找到地址,这个例子可以很形象的解释map命名的由来和功能。
- map:接受一个Function作为参数,即输入一个值,返回另一个值,满足转换的语义。
- flatmap:同样接受一个Function作为参数,不同的是这个Function中有一个参数是一个stream,返回的也是一个stream,意为将多个stream连成一个stream。
同样,举个简单的例子:
Stream.of("d2", "a2", "b1", "a3", "c1", "a4", "a2", "a1").filter(x -> x.startsWith("a")).map(String::toUpperCase).forEach(System.out::println);
//output
A2
A3
A4
A2
A1List<String> list = Stream.of("Hello", "world!").map(s -> s.split("")).flatMap(Arrays::stream).collect(Collectors.toList());
System.out.println(list);
//output
[H, e, l, l, o, w, o, r, l, d, !]
4. 查找和匹配
这里的能力可以认为是一个加强版的contains方法,具备多种查找匹配能力。
- allMatch:返回boolean,接受一个Predicate断言,确认全部元素均满足这个条件则返回true,否则返回false
- anyMatch:与allMatch类似,但从语义上可以区分只要任意元素满足条件即可
- noneMatch:同样,要求没有任何元素满足条件
- findFirst:返回一个Optional,里面是满足条件的第一个元素
- findAny:返回Optional,里面是满足条件的任一元素
这里需要解惑的是findAny与findFirst的区别,因为这两个都是找到满足条件的元素就返回,但findFirst会在限制并行流的计算,会严格按照集合中元素的顺序来依次查找。findAny就不会有这个限制。如果非并行计算场景,这二者并无区别。
下面依旧举简单的例子说明:
boolean b1 = Stream.of("d2", "a2", "b1", "a3", "c1", "a4", "a2", "a1").anyMatch(x -> x.startsWith("a"));System.out.println(b1);
//output: trueString s2 = Stream.of("d2", "a2", "b1", "a3", "c1", "a4", "a2", "a1").filter(x -> x.startsWith("a")).findFirst().get();System.out.println(s2);
//output: a2String s3 = Stream.of("d2", "a2", "b1", "a3", "c1", "a4", "a2", "a1").filter(x -> x.startsWith("a")).findAny().get();System.out.println(s3);
//output: a2//换成并行流
String s4 = Stream.of("d2", "a2", "b1", "a3", "c1", "a4", "a2", "a1").parallel().filter(x -> x.startsWith("a")).findFirst().get();System.out.println(s4);
//output: a2String s5 = Stream.of("d2", "a2", "b1", "a3", "c1", "a4", "a2", "a1").parallel().filter(x -> x.startsWith("a")).findAny().get();System.out.println(s5);
//output: a4
5. 归约
归约是一个比较复杂的数学理论,通常是用于将一个未知的问题转换成另一些已知问题,同时这些已知的问题和未知的问题存在某种关联。这里不做详细探讨。在Stream API有一些方法就是用的类似的归约的思想,将大的集合计算分解成小的函数计算并最终合成结果。
- reduce
- collect
这两个方法都很重要,且都是终端操作,执行完即返回流的计算结果。我们逐个来说,先看reduce。reduce的英文含义为减少、归纳,在stream接口中的定义如下:
T reduce(T identity, BinaryOperator<T> accumulator);Optional<T> reduce(BinaryOperator<T> accumulator);<U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);
这样的方法签名如同天书,先看一个简单的例子:
Integer i = Stream.of(1, 4, 6, 7, 9).reduce(1, (sum, i) -> sum + i);
System.out.println(i);
其中reduce我传了2个参数:
- 1表示初始值,可以不给,不给的话默认从流的第一个元素开始计算,但返回是就是Optional
- (sum, i) -> sum + i表示计算函数,每次计算的结果都会暂存在sum中,i则是下一个元素
所以总的来说,这是一个迭代归纳的过程,将多个元素的流按照自己制定的计算规则变成一个元素。不仅仅可以做述职运算,也可以实现复杂对象的转换,先看例子(此例来源于与廖雪峰老师的网站并稍做修改,具体链接:https://www.liaoxuefeng.com/wiki/1252599548343744/1322402971648033):
List<String> props = Lists.newArrayList("profile=native", "debug=true", "logging=warn", "interval=500");Map<String, String> map = props.stream().map(kv -> {String[] ss = kv.split("=", 2);Map<String, String> m = Maps.newHashMap();m.put(ss[0], ss[1]);return m;}).reduce(new HashMap<>(), (m, kv) -> {m.putAll(kv);return m;});map.forEach((k, v) -> System.out.println(k + " = " + v));//output:
logging = warn
interval = 500
debug = true
profile = native
第一个map执行完之后返回了多个小map这里使用reduce进行一个map的累加:
- new HashMap<>()是初始值,一个空map
- (m, kv) ->中,m是暂存累加结果,kv表示下一个元素map
以上看来,reduce的使用场景应该会很广泛,尤其是多个集合合成一个大集合的场景。
小结
本文介绍了stream是什么、创建stream的方法、stream的一些基本API的能力和reduce方法的使用。作为stream最佳实践的开篇,先从stream的基础开始写,后续会逐步深入并总结我个人使用下来的最佳实践,希望大家持续关注,共同学习。
相关文章:
Java8的Stream最佳实践
从这一篇文章开始,我们会由浅入深,全面的学习stream API的最佳实践(结合我的使用经验),本想一篇写完,但写着写着发现需要写的内容太多了,所以分成一个系列慢慢来说。给大家分享我的经验的同时&a…...
Spark SQL函数定义
目录 窗口函数 SQL函数分类 Spark原生自定义UDF函数 Pandas的UDF函数 Apache Arrow框架基本介绍 基于Arrow完成Pandas DataFrame和Spark DataFrame互转 基于Pandas完成UDF函数 自定义UDF函数 自定义UDAF函数 窗口函数 分析函数 over(partition by xxx order by xxx [as…...
触摸屏监控双速电动机-PLC I/O电路设计
PLC的输入接线电路图 PLC的输入接线电路如图1-21所示。24VDC电源选用0.7mm2的棕色和蓝色软铜导线,弱电信号线用0.5~0.7mm2的黑色或者白色软铜导线。 PLC输入接线图 PLC的输出接线电路图 PLC的输出接线电路如图1-22所示。AC220V接触器型号为CJX2-12,线…...
idea中使用git提交代码报 Nothing To commit No changes detected
问题描述 在idea中右键,开始将变更的代码进行提交的时候,【Commit Directory】点击提交的时候 报 Nothing To commit No changes detected解决方案 在这里点击Test 看看是不是能下面显示git版本,不行的话 会显示一个 fix的字样,行…...
基于长短期神经网络的回归分析,基于LSTM的回归预测
目录 背影 摘要 LSTM的基本定义 LSTM实现的步骤 基于长短期神经网络LSTM的回归分析 MATALB代码:基于长短期神经网络的回归分析,基于LSTM的回归预测资源-CSDN文库 https://download.csdn.net/download/abc991835105/88184633 效果图 结果分析 展望 参考论文 背影 LSTM神经…...
mac查看maven版本报错:The JAVA_HOME environment variable is not defined correctly
终端输入mvn -version报错: The JAVA_HOME environment variable is not defined correctly, this environment variable is needed to run this program. Java环境变量的问题,打开bash_profile查看 open ~/.bash_profile export JAVA_8_HOME/Library/Java/JavaVirtualMachine…...
蓝桥杯省赛无忧 编程9
#include<bits/stdc.h> using namespace std; int main() {int n,k,ans0;cin>>n>>k;while(n--){int a;cin>>a;ansa&1;}if(ans&1) cout<<"Alice"<<\n;else cout<<"Bob"; return 0; }这个游戏是基于数…...
Spring data都包含哪些内容
Spring Data是一个涵盖了对多种数据库访问技术的支持的项目集合,旨在提供一致的数据访问方式,简化数据访问层(DAO层)的开发工作。Spring Data项目为许多不同类型的数据存储提供了易于使用的接口和模式。主要包括以下几个方面&…...
unity 利用Graphics.Blit来制作图片效果
c# 的代码 using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI;public class GraphicsBlitTest : MonoBehaviour {public Texture2D source;//原纹理public Material material;//效果材质public RawImage rawImage;// Sta…...
Linux ---- 小玩具
目录 一、安装: 1、佛祖保佑,永不宕机,永无bug 2、小火车 3、艺术字和其它 天气预报 艺术字 4、会说话的小牦牛 5、其他趣味图片 我爱你 腻害 英雄联盟 帅 忍 龙 你是猪 福 好运连连 欢迎 加油 想你 忘不了你 我错了 你…...
练习题 有奖问答
题目 问题描述 小蓝正在参与一个现场问答的节目。活动中一共有 3030 道题目, 每题只有答对和答错两种情况, 每答对一题得 10 分,答错一题分数归零。 小蓝可以在任意时刻结束答题并获得目前分数对应的奖项,之后不能再答任何题目。最高奖项需要 100 分,…...
php 文件操作
目录 1.file_xxx 2.fopen 1.file_xxx 文件读写的内容都是字符串数据格式 readfile(); //读取文件内容,并返回文件的长度 file_get_contents(文件路径); //读取文件。支持本地文件和远程文件url file_put_contents(文件路径, 内容); //写入数据,保存…...
Next-GPT: Any-to-Any Multimodal LLM
Next-GPT: Any-to-Any Multimodal LLM 最近在调研一些多模态大模型相关的论文,发现Arxiv上出的论文根本看不过来,遂决定开辟一个新坑《一页PPT说清一篇论文》。自己在读论文的过程中会用一页PPT梳理其脉络和重点信息,旨在帮助自己和读者快速了…...
Angular系列教程之MVC模式和MVVM模式
文章目录 MVC模式MVVM模式MVC与MVVM的区别Angular如何实现MVVM模式总结 在讨论Angular的时候,我们经常会听到MVC和MVVM这两种设计模式。这两种模式都是为了将用户界面(UI)和业务逻辑分离,使得代码更易于维护和扩展。在这篇文章中,我们将详细介…...
windows虚拟主机和linux虚拟主机的区别有哪些?
很多个人站长和中小企业在做网站的时候,会选择虚拟主机。虚拟主机用的操作系统多为Windows系统,很多人一提到操作系统立马联想到Windows系统。其实除了Windows系统外,还有很多的操作系统。其中Linux系统是其中的佼佼者。 1、操作系统 window…...
微信小程序(七)navigator点击效果
注释很详细,直接上代码 上一篇 新增内容: 1.默认效果 2.无效果 3.激活效果 源码: index.wxml //如果 <navigator url"/pages/logs/logs">跳转到log页面(默认) </navigator><navigator url&q…...
腾讯云服务器价格查询,2024更新
腾讯云服务器租用优惠价格表:轻量应用服务器2核2G3M价格62元一年、2核2G4M价格118元一年,540元三年、2核4G5M带宽218元一年,2核4G5M带宽756元三年、轻量4核8G12M服务器646元15个月;云服务器CVM S5实例2核2G配置280.8元一年、2核4G…...
更适合3D项目的UI、事件交互!纯国产数字孪生引擎持续升级中!!!
UI和事件交互是3D可视化项目中最常见的模块,主要用于信息添加、展示,用来确保按照用户需求呈现内容并完成交互。 平时工作在进行UI和交互设计时,经常出现以下问题:UI过于复杂导致3D项目内交互效率低下,或者是结合3D项目…...
OpenCV-Python(47):支持向量机
原理 线性数据分割 如下图所示,其中含有两类数据,红的和蓝的。如果是使用kNN算法,对于一个测试数据我们要测量它到每一个样本的距离,从而根据最近的邻居分类。测量所有的距离需要足够的时间,并且需要大量的内存存储训…...
Centos 8 安装 Elasticsearch
简介:CentOS 8是一个基于Red Hat Enterprise Linux(RHEL)源代码构建的开源操作系统。它是一款稳定、可靠、安全的服务器操作系统,适合用于企业级应用和服务的部署。CentOS 8采用了最新的Linux内核和软件包管理系统,提供…...
地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...
解锁数据库简洁之道:FastAPI与SQLModel实战指南
在构建现代Web应用程序时,与数据库的交互无疑是核心环节。虽然传统的数据库操作方式(如直接编写SQL语句与psycopg2交互)赋予了我们精细的控制权,但在面对日益复杂的业务逻辑和快速迭代的需求时,这种方式的开发效率和可…...
深入理解JavaScript设计模式之单例模式
目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式(Singleton Pattern&#…...
oracle与MySQL数据库之间数据同步的技术要点
Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异,它们的数据同步要求既要保持数据的准确性和一致性,又要处理好性能问题。以下是一些主要的技术要点: 数据结构差异 数据类型差异ÿ…...
华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建
华为云FlexusDeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色,华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型,能助力我们轻松驾驭 DeepSeek-V3/R1,本文中将分享如何…...
【C++特殊工具与技术】优化内存分配(一):C++中的内存分配
目录 一、C 内存的基本概念 1.1 内存的物理与逻辑结构 1.2 C 程序的内存区域划分 二、栈内存分配 2.1 栈内存的特点 2.2 栈内存分配示例 三、堆内存分配 3.1 new和delete操作符 4.2 内存泄漏与悬空指针问题 4.3 new和delete的重载 四、智能指针…...
Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
GO协程(Goroutine)问题总结
在使用Go语言来编写代码时,遇到的一些问题总结一下 [参考文档]:https://www.topgoer.com/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/goroutine.html 1. main()函数默认的Goroutine 场景再现: 今天在看到这个教程的时候,在自己的电…...
从物理机到云原生:全面解析计算虚拟化技术的演进与应用
前言:我的虚拟化技术探索之旅 我最早接触"虚拟机"的概念是从Java开始的——JVM(Java Virtual Machine)让"一次编写,到处运行"成为可能。这个软件层面的虚拟化让我着迷,但直到后来接触VMware和Doc…...
WEB3全栈开发——面试专业技能点P7前端与链上集成
一、Next.js技术栈 ✅ 概念介绍 Next.js 是一个基于 React 的 服务端渲染(SSR)与静态网站生成(SSG) 框架,由 Vercel 开发。它简化了构建生产级 React 应用的过程,并内置了很多特性: ✅ 文件系…...
