在这篇文章中,我们将看到Java 8流的深入概述,其中包含许多示例和练习。
目录
介绍
You may think that Stream must be similar to InputStream
or OutputStream
, but that’s not the case.
A 流
represents a sequence of elements supporting sequential and parallel aggregate operations. Stream does not store data, it operates on source data structures such as List, 采集,数组等
大多数流操作接受 功能接口 这使其成为lambda表达式的理想选择。
如果您不熟悉功能接口,lambda表达式和竞彩篮球分析引用,则可能需要先阅读以下教程,然后再继续。
流操作的类型
流操作有两种类型。
Intermediate operations:
返回一个流,该流可以与其他通过dot进行的中间操作链接在一起。Terminal operations:
返回void或非流输出。
让’通过简单的示例了解。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 包 组织.Arpit.爪哇2blog.流; 进口 爪哇.实用程序.数组; 进口 爪哇.实用程序.清单; 上市 类 流操作 { 上市 静态的 虚空 主要(串[] args) { 清单<串> stringList = 数组.asList(“约翰”, “马丁”, “玛丽”, “史蒂夫”); stringList.流() .地图((s) -> s.至大写()) .每次(系统.出::打印); } } |
输出:
MARTIN
MARY
STEVE
这里,
To perform a computation, stream operations are built into a Stream pipeline. 流 pipeline
consists of:
source
zero or more intermediate operations
terminal operation
.
在我们的示例中,Stream管道包括:
Source
:字符串列表
1 Intermediate operation
:地图
1 terminal operation
:forEach
下图将使其更加清晰。
地图
is intermediate operation and 前言
is terminal opertion.
大多数流操作接受描述用户定义的行为的参数,例如 lambda表达式 地图((s)->s.toUpperCase())
传递给地图操作。
为了获得正确的行为,流参数应为:
non-interfering:
在执行Stream管线时,不应修改流源。您可以了解更多有关 干扰.
Stateless:
在大多数情况下,lambda表达式应该是无状态的。其输出不应取决于在Stream流水线执行期间可能改变的状态。我已经讲过 有状态lambda表达式 在并行流教程中。
流创建
有多种创建流的竞彩篮球分析。
空流
空的()
竞彩篮球分析可用于创建空流。
1 2 3 | 流 s = 流.空的() |
通常用于返回零元素而不是null的Stream。
收集流
流 can be created from Collection by calling .stream()
or .parallelStream()
1 2 3 4 5 6 7 | 清单 stringList=数组.asList(“安迪”,“彼得”,“艾米”,“玛丽”); stringList.流() .地图((s)->s.至大写()) .每次(系统.出::打印); |
stringList.stream()
将返回常规对象流。
流
你不’无需创建集合即可获取Stream。您也可以使用 。的()
1 2 3 | 流 流Array =流.的(“X”,“ Y”,“ Z”); |
流.generate()
生成()竞彩篮球分析接受 供应商 用于元素生成。它创建无限的Stream,您可以通过调用limit()函数对其进行限制。
1 2 3 4 5 6 7 8 9 10 | 流<整数> 整型Stream=流.生成(() -> 1).限制(5); 整型Stream.每次(系统.出::打印); //输出 // 1 // 1 // 1 // 1 // 1 |
这将创建具有10个值为1的Integer流。
流.iterate()
流.iterate()也可用于生成无限流。
1 2 3 4 5 6 7 8 9 10 | 流<整数> 整型Stream = 流.重复(100 , n -> n+1).限制(5); 整型Stream.每次(系统.出::打印); //输出 // 100 // 101 // 102 // 103 // 104 |
First parameter of iterate method represents first element of the Stream. All the following elements will be generated by lambda expression n->n+1
and limit() is used to convert infinite Stream to finite Stream with 5 elements.
懒惰评估
流是懒惰的;在遇到终端操作之前,不执行中间操作。
每个中间操作都会生成一个新流,并存储提供的操作或功能。当调用终端操作时,流管线执行开始,并且所有中间操作都一一执行。
让’借助示例可以理解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 流<串> 名称Stream = 流.的(“莫汉”,“约翰”,“ vaibhav”,“阿米特”); 流<串> 名称StartJ = 名称Stream.地图(串::至大写) .窥视( e -> 系统.出.打印(e)) .过滤(s -> s.以。。开始(“ J”)); 系统.出.打印(“呼叫终端操作:计数”); 长 计数 = 名称StartJ.计数(); 系统.出.打印(“数:”+ 计数); //输出 //调用终端操作:计数 // MOHAN // 约翰 // VAIBHAV // AMIT //计数:1 |
在前面的输出中,您可以看到,除非且直到调用了终端操作计数,否则控制台上什么都没有打印。
In the preceding example, peek() method is used to print the element of stream. 窥视()
method is generally used for logging and debugging purpose only.
操作顺序
让’看流如何处理操作顺序。
您能猜出程序的输出吗?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 流<串> 名称Stream = 流.的(“莫汉”,“约翰”,“ vaibhav”,“阿米特”); 流<串> 名称StartJ = 名称Stream.地图( (s) -> { 系统.出.打印(“地图:”+s); 返回 s.至大写(); }) .过滤( (s) -> { 系统.出.打印(“过滤器:”+s); 返回 s.以。。开始(“ J”); } ); 可选的<串> findAny = 名称StartJ.findAny(); 系统.出.打印(“最终输出:”+findAny.得到()); |
输出将是:
Filter: MOHAN
Map: john
Filter: JOHN
JOHN
这里的操作顺序可能令人惊讶。一种常见的竞彩篮球分析是对所有元素执行中间操作,然后执行下一个操作,但每个元素都垂直移动。
这种行为可以减少实际的操作次数。
例如:
In preceding example, Strings vaibhav
and amit
did not go through 地图
and 过滤
operation as we already got result(findAny()
) with String john
.
一些中间操作,例如 已排序 are executed on the entire collection. As succeding operations might depend on the result of 已排序
operation.
原始流
除了常规流之外, 爪哇 8 还为int,long和double提供原始Stream。
原始流是:
- Int的IntStream
- 长流长
- DoubleStream双
所有原始流都与常规流类似,但有以下区别。
- It supports few terminal aggregate functions such
和()
,平均()
, etc. - 它接受专用功能接口,例如IntPredicate代替Predicate,IntConsumer代替Consumer。
这是IntStream的示例。
1 2 3 4 5 6 7 8 | 整型 和 = 数组.流(新 整型[] {1,2,3}) .和(); 系统.出.打印(和); //输出 // 6 |
将Stream转换为IntStream
You may need to convert Stream to IntStream to perform terminal aggregate operations such as sum or average. You can use 地图ToInt()
, 地图ToLong()
or 地图ToDouble()
method to convert Stream to primitive Streams.
Here is an example:
1 2 3 4 5 6 7 8 | 流.的("10","20","30") .地图ToInt(整数::parseInt) .平均() .如果存在(系统.出::打印); //输出 // 20.0 |
将IntStream转换为流
You may need to convert IntStream to Stream to use it as any other datatype. You can use 地图ToObj()
convert primitive Streams to regular Stream.
Here is an example:
1 2 3 4 5 6 7 8 | 串 收藏 = 串流.的(10,20,30) .地图ToObj((i)->” +i) .收藏(收藏家.加盟(“-”)); 系统.出.打印(收藏); //输出 // 10-20-30 |
员工阶层
Consider a 雇员
类 which has two fields 名称
, 年龄
, listOfCities
.
Here listOfCities
denotes cities in which Employee has lived so far.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | 包 组织.Arpit.爪哇2blog.流; 进口 爪哇.实用程序.清单; 上市 类 雇员 实施 可比<雇员>{ 私人的 串 名称; 私人的 整型 年龄; 私人的 清单<串> listOfCities; 上市 雇员(串 名称, 整型 年龄,清单<串> listOfCities) { 超(); 这个.名称 = 名称; 这个.年龄 = 年龄; 这个.listOfCities=listOfCities; } 上市 串 得到Name() { 返回 名称; } 上市 虚空 setName(串 名称) { 这个.名称 = 名称; } 上市 整型 得到Age() { 返回 年龄; } 上市 虚空 setAge(整型 年龄) { 这个.年龄 = 年龄; } 上市 清单<串> 得到ListOfCities() { 返回 listOfCities; } 上市 虚空 setListOfCities(清单<串> listOfCities) { 这个.listOfCities = listOfCities; } @覆写 上市 串 toString() { 返回 “员工[name =“ + 名称 + “,age =” + 年龄 + “]”; } @覆写 上市 整型 相比于(雇员 o) { 返回 这个.得到Name().相比于(o.得到Name()); } } |
This 雇员
类 will be used in all succeeding examples.
Let’s create 员工名单
on which we are going to perform intermediate and terminal operations.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | 包 组织.Arpit.爪哇2blog.流; 进口 爪哇.实用程序.数组列表; 进口 爪哇.实用程序.数组; 进口 爪哇.实用程序.清单; 上市 类 流GetListOfEmployees { 上市 静态的 虚空 主要(串[] args) { 清单<雇员> 员工名单=得到ListOfEmployees(); //在此处编写流代码 } 上市 静态的 清单<雇员> 得到ListOfEmployees() { 清单<雇员> 员工名单 = 新 数组列表<>(); 雇员 e1 = 新 雇员(“莫汉”, 24,数组.asList(“纽约”,孟加拉国)); 雇员 e2 = 新 雇员(“约翰”, 27,数组.asList(“巴黎”,“伦敦”)); 雇员 e3 = 新 雇员(“ Vaibhav”, 32,数组.asList(“浦那”,“西雅图”)); 雇员 e4 = 新 雇员(“授予”, 22,数组.asList(“金奈”,“海得拉巴”)); 员工名单.加(e1); 员工名单.加(e2); 员工名单.加(e3); 员工名单.加(e4); 返回 员工名单; } } |
常见的中间操作
地图()
地图()操作 用于转换流<T> to Stream<R>. It produces one output result of type 'R'
for each input value of type 'T'
. It takes 功能 接口作为参数。
For example:
You have stream of list of employees and you need a list of employee names, you simply need to convert 流<Employee>
to 流<String>
.
1 2 3 4 5 6 7 8 9 | 清单<串> 员工姓名 = 员工名单.流() .地图(e -> e.得到Name()) .收藏(收藏家.到清单()); 系统.出.打印(员工姓名); //输出 // [Mohan,John,Vaibhav,Amit] |
您也可以使用map,即使它产生相同类型的结果。
In case, you want employee name in uppercase, you can use another 地图()
function to convert string to uppercase.
1 2 3 4 5 6 7 8 9 10 | 清单<串> 员工姓名 = 员工名单.流() .地图(e -> e.得到Name()) .地图(s -> s.至大写()) .收藏(收藏家.到清单()); 系统.出.打印(员工姓名); //输出 // [MOHAN,JOHN,VAIBHAV,AMIT] |
过滤()
Filter()操作 用于根据条件过滤流。 Filter竞彩篮球分析采用Predicate()接口,该接口返回布尔值。
Let’例如,您要向以名字开头的员工‘A’.
您可以编写以下功能代码来实现相同的目的。
1 2 3 4 5 6 7 8 9 10 | 清单<串> 员工姓名 = 员工名单.流() .地图(e -> e.得到Name()) .过滤(s -> s.以。。开始(“一个”)) .收藏(收藏家.到清单()); 系统.出.打印(员工姓名); //输出 // [AMIT] |
[![StreamFilter]
已排序()
您可以使用 已排序() 对对象列表进行排序的竞彩篮球分析。不带参数的sorted竞彩篮球分析以自然顺序对列表进行排序。 sorted()竞彩篮球分析还接受比较器作为参数来支持自定义排序。
💡 你知道吗?
自然顺序意味着根据列表元素类型实现的可比接口对列表进行排序。
例如:
清单<Integer>
将根据Integer类实现的可比较接口进行排序。
这是sorted()竞彩篮球分析的示例
1 2 3 4 5 6 7 8 9 | 清单<雇员> 雇员 = 员工名单.流() .已排序() .收藏(收藏家.到清单()); 系统.出.打印(雇员); //输出 // [员工[name = Amit,年龄= 22],员工[name = John,年龄= 27],员工[name = Mohan,年龄= 24],员工[name = Vaibhav,年龄= 32]] |
Here is the 已排序()
method example with 比较器 as a parameter.
1 2 3 4 5 6 7 8 9 | 清单<雇员> 雇员 = 员工名单.流() .已排序((e1,e2)->e1.得到Age() - e2.得到Age()) .收藏(收藏家.到清单()); 系统.出.打印(雇员); //输出 // [雇员[name = Amit,年龄= 22],雇员[name = Mohan,年龄= 24],雇员[name = John,年龄= 27],雇员[name = Vaibhav,年龄= 32]] |
您也可以使用 竞彩篮球分析参考 as below:
1 2 3 4 5 6 7 8 9 | 清单<雇员> 雇员 = 员工名单.流() .已排序(比较器.比较(雇员::得到Age)) .收藏(收藏家.到清单()); 系统.出.打印(雇员); //输出 // [雇员[name = Amit,年龄= 22],雇员[name = Mohan,年龄= 24],雇员[name = John,年龄= 27],雇员[name = Vaibhav,年龄= 32]] |
限制()
您可以使用limit()限制流中元素的数量。
For example:
限制(3)
返回列表中的前3个元素。
让’借助示例查看:
1 2 3 4 5 6 7 8 9 | 清单<雇员> 雇员 = 员工名单.流() .限制(3) .收藏(收藏家.到清单()); 系统.出.打印(雇员); //输出 // [员工[name = Mohan,年龄= 24],员工[name = John,年龄= 27],员工[name = Vaibhav,年龄= 32]] |
跳跃()
跳跃(int n)
竞彩篮球分析用于丢弃流中的前n个元素。
For example:
跳跃(3)
从流中丢弃前3个元素。
让’借助示例查看:
1 2 3 4 5 6 7 8 9 | 清单<雇员> 雇员 = 员工名单.流() .跳跃(3) .收藏(收藏家.到清单()); 系统.出.打印(雇员); //输出 // [Employee [name = Amit,age = 22]] |
flatmap()
地图()
运算为每个输入元素生成一个输出。
如果每个输入都需要多个输出怎么办?
flatmap()操作 正是用于此目的。它用于为每个输入映射多个输出。
例如:
我们要累积所有员工居住的城市清单。一名员工可能居住在多个城市,因此每个员工可能拥有一个以上的城市。
让’借助示例查看:
1 2 3 4 5 6 7 8 9 10 | 清单<串> listOfCities = 员工名单.流() .flatMap(e -> e.得到ListOfCities().流()) .收藏(收藏家.到清单()); 系统.出.打印(“ listOfCities:” +listOfCities); //输出 // listOfCities:[纽约,孟加拉,巴黎,伦敦,浦那,西雅图,金奈,海得拉巴] |
常用终端操作
前言
前言() 是终端操作,用于迭代对象的收集/流。它需要 消费者 as a parameter.
让’s说您要打印流中的元素。
1 2 3 4 5 6 7 8 9 10 | 员工名单.流() .每次(系统.出::打印); //输出 //员工[姓名= Mohan,年龄= 24] //员工[姓名=约翰,年龄= 27] //员工[姓名= Vaibhav,年龄= 32] //员工[name = Amit,年龄= 22] |
收藏
收藏()
是终端操作,使用收集器对Stream的元素执行可变还原。 收藏家 是提供内置收集器的实用程序类。
例如:
收藏家.toList()
提供了一个收集器,该收集器将Stream转换为列表对象。
以下代码将员工姓名累积到 数组列表
1 2 3 4 5 6 7 8 9 | 清单<串> 员工姓名 = 员工名单.流() .地图(雇员::得到Name) .收藏(收藏家.到清单()); 系统.出.打印(员工姓名); //输出 // [Mohan,John,Vaibhav,Amit] |
减少
归约运算结合了Stream的所有元素并产生单个结果。
爪哇 8有3个重载版本的reduce竞彩篮球分析。
-
可选的<T> reduce(BinaryOperator<T> accumulator):
This method takesBinaryOperator
accumulator function.BinaryOperator
isBiFunction
where both the operands are of same type. First parameter is result till current execution, and second parameter is the current element of the Stream.让’查找年龄最小的人的名字。
1234567员工名单.流().减少( (e1,e2)-> (e1.得到Age() < e2.得到Age()? e1:e2)).如果存在(系统.出::打印);//输出//员工[name = Amit,年龄= 22] T reduce(T identity, BinaryOperator<T> accumulator):
此竞彩篮球分析具有标识值和累加器功能。同一性值是减少量的初始值。如果Stream为空,则结果为identity。
Let’查找所有年龄段的员工的总和123456789整型 总年龄 = 员工名单.流().地图ToInt(雇员::得到Age).减少(0, (年龄1,年龄2)-> (年龄1 + 年龄2));系统.出.打印(“所有员工的年龄总和:”+总年龄);//输出//所有员工的年龄总和:105-
<U> U reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner):
该竞彩篮球分析采用标识值和累加器功能以及组合器。合并器主要用于以下情况 并行流。合并器合并并行运行的子流的结果。
计数
计数()用于计算流中元素的数量。
1 2 3 4 5 6 7 8 9 10 | 长 empCountStartJ = 员工名单.流() .地图(雇员::得到Name) .过滤(s -> s.以。。开始(“ J”)) .计数(); 系统.出.打印(empCountStartJ); //输出 // 1 |
allMatch()
allMatch()
当流中的所有元素都满足提供的条件时,返回true。
这是一种短路端子操作,因为一旦遇到任何不匹配的元素,操作就会立即停止。
1 2 3 4 5 6 7 8 9 | 布尔值 allMatch = 员工名单.流() .allMatch(e ->e.得到Age()>18); 系统.出.打印(“所有受雇的成年人都是:” +allMatch); //输出 //所有雇员都是成年人吗? |
nonMatch()
nonMatch()
当流中的所有元素都不满足提供的条件时,返回true。
这是一种短路端子操作,因为一旦遇到任何匹配的元素,操作就会停止。
1 2 3 4 5 6 7 8 9 | 布尔值 noneMatch = 员工名单.流() .noneMatch(e ->e.得到Age()>60); 系统.出.打印(“所有的雇员年龄都在60岁以下:” +noneMatch); //输出 //所有员工都在60岁以下:是 |
anyMatch()
anyMatch()
当流中的任何元素满足提供的条件时,返回true。
这是一种短路端子操作,因为一旦遇到任何匹配的元素,操作就会停止。
1 2 3 4 5 6 7 8 9 | 布尔值 anyMatch = 员工名单.流() .anyMatch(e ->e.得到Age()>30); 系统.出.打印(“任何员工的年龄大于30岁:” +anyMatch); //输出 //任何员工的年龄大于30:true |
分()
分(Comparator)
根据提供的比较器返回流中的最小元素。它返回一个包含实际值的对象。
1 2 3 4 5 6 7 8 9 10 | 可选的<雇员> 分EmpOpt = 员工名单.流() .分(比较器.比较(雇员::得到Age)); 雇员 分AgeEmp = 分EmpOpt.得到(); 系统.出.打印(“最低年龄的雇员是:” +分AgeEmp); //输出 //年龄最小的员工是:Employee [name = Amit,age = 22] |
最高()
最高(Comparator)
根据提供的比较器返回流中的最大元素。它返回一个包含实际值的对象。
1 2 3 4 5 6 7 8 9 10 | 可选的<雇员> 最高EmpOpt = 员工名单.流() .最高(比较器.比较(雇员::得到Age)); 雇员 最高AgeEmp = 最高EmpOpt.得到(); 系统.出.打印(“年龄最大的雇员是:” +最高AgeEmp); //输出 //年龄上限为的员工为:员工[name = Vaibhav,年龄= 32] |
并行流
You can create Parallel Stream using .parallel()
method on 流
object in java.
Here is an example:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 整型[] 数组= {1,2,3,4,5}; 系统.出.打印(“ ================================”); 系统.出.打印(“使用并行流”); 系统.出.打印(“ ================================”); 串流 整型ParallelStream=数组.流(数组).平行(); 整型ParallelStream.每次((s)-> { 系统.出.打印(s+”+线.currentThread().得到Name()); } ); |
这是一篇关于 爪哇 8并行流.
练习题
让’在Stream上练习一些练习。
练习1
练习2
给定雇员列表,找到年龄大于25岁的雇员人数?
回答:
练习3
Given the list of employees, find the employee whose name is John
.
练习4
给定雇员列表,您需要找到雇员的最高年龄吗?
回答:
练习5
给定雇员列表,您需要按年龄对雇员列表进行排序?仅使用Java 8 API
回答:
练习6
给定雇员列表,您需要将所有雇员姓名与","?
回答:
练习7
根据员工列表,您需要按姓名分组