前言
如果有些朋友以前没有使用过java8 stream这种链式编程方式做开发,想学习一下。
如果有些朋友只学习了一部分用法,想学习更多。
如果有些朋友想看看有没有好的示例适用于实际工作当中。
那么恭喜你,这篇文章非常适合你。
首先,我们一起看看stream的继承关系:
Stream、IntStream、LongStream、DoubleStream的父接口都是BaseStream。BaseStream的四个子接口方法都差不多,只是IntStream、LongStream、DoubleStream直接存储基本类型,可以避免自动装/拆箱,效率会更高一些。但是,我们实际上使用Stream更多一些。
我们再看看stream的工作流程图:
为什么要学stream的链式编程方式
业务需求1:指定一个字符串数组,找出里面相同的元素,并且统计重复的次数。
我们以前大概是这样做的:
1 | java复制代码public class CountTest { |
执行结果:
1 | java复制代码key:a value:3 |
我们再看看如果用java8的stream可以怎么做:
1 | java复制代码public class CountTest { |
执行结果:
1 | makefile复制代码key:a value:3 |
我们可以看到testCount1和testCount2执行结果相同,仅仅一行代码:
1 | ini复制代码Map<String, Long> countMap = list.stream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting())); |
就可以实现上面testCount1中多行代码的逻辑。
业务需求2:从一个指定的字符串数组中,查找指定的字符串是否存在
我们以前大概是这样做的:
1 | ini复制代码public class FindTest { |
我们再看看如果用java8的stream可以怎么做:
1 | typescript复制代码public class MatchTest { |
我们可以看到调用testFind1和testFind2方法执行结果也是一样的。但是,用java8 stream的语法,又只用一行代码就完成功能了,真棒。
java8 stream超详细用法指南
stream的操作符大体上分为两种:中间操作符和终止操作符
中间操作:
1.filter(T-> boolean)
过滤数据,保留 boolean 为 true 的元素,返回一个集合
1 | java复制代码public class FilterTest { |
collect(Collectors.toList())可以把流转换为 List 类型,collect实际上是一个终止操作。
2.map(T -> R)
转换操作符,可以做数据转换,比如:把字符串转换成int、long、double,或者把一个实体转换成另外一个实体。包含:map,mapToInt、mapToLong、mapToDouble
1 | java复制代码public class MapTest { |
3.flatMap(T -> Stream)
将流中的每一个元素 T 映射为一个流,再把每一个流连接成为一个流
1 | java复制代码public class FlatMapTest { |
我们可以看到flatMap可以轻松把字符串的二维数据变成一位数组。
4.distinct
去重,类似于msql中的distinct的作用,底层使用了equals方法做比较。
1 | java复制代码public class DistinctTest { |
其实,去重还有另外一种办法,可以用Collectors.toSet(),后面会讲到。
5.sorted
对元素进行排序,前提是实现Comparable接口,当然也可以自定义比较器。
1 | java复制代码public class SortTest { |
6.limit
限流操作,有点类似于mysql中的limit功能,比如:有10个元素,只取前面3个元素
1 | java复制代码public class LimitTest { |
7.skip
跳过操作,比如:有个10个元素,从第5个元素开始去后面的元素
1 | java复制代码public class SkipTest { |
8.peek
挑出操作,
1 | java复制代码public class PeekTest { |
眼尖的朋友会发现,进行x.toUpperCase()转换为大写功能,但是实际上没有生效。把peek改成map方法试试:
1 | java复制代码public class PeekTest { |
我们可以看到,用map操作转换成大写功能生效了,但是用peek操作却没有生效。peek只是对Stream中的元素进行某些操作,但是操作之后的数据并不返回到Stream中,所以Stream中的元素还是原来的元素。
终止操作:
1.forEach
遍历操作,包含:forEach 和 forEachOrdered
forEach:支持并行处理
forEachOrdered:是按顺序处理的,遍历速度较慢
1 | java复制代码public class ForEachTest { |
2.collect
收集操作,将所有的元素收集起来,Collectors 提供了非常多收集器。包含:toMap、toSet、toList、joining,groupingBy,maxBy,minBy等操作。
toMap:将数据流转换为map,里面包含的元素是用key/value的形式的
toSet:将数据流转换为set,里面包含的元素不可重复
toList:将数据流转出为list,里面包含的元素是有序的
joining:拼接字符串
groupingBy:分组,可以将list转换map
couting:统计元素数量
maxBy:获取最大元素
minBy:获取最小元素
summarizingInt: 汇总int类型的元素,返回IntSummaryStatistics,再调用具体的方法对元素进行统计:getCount(统计数量),getSum(求和),getMin(获取最小值),getMax(获取最大值),getAverage(获取平均值)
summarizingLong:汇总long类型的元素,用法同summarizingInt
summarizingDouble:汇总double类型的元素,用法同summarizingInt
averagingInt:获取int类型的元素的平均值,返回一个double类型的数据
averagingLong:获取long类型的元素的平均值,用法同averagingInt
averagingDouble:获取double类型的元素的平均值,用法同averagingInt
mapping:获取映射,可以将原始元素的一部分内容作为一个新元素返回
1 | java复制代码public class CollectTest { |
3.find
查找操作,包含:findFirst、findAny
findFirst:找到第一个,返回的类型为Optional
findAny:使用 stream() 时找到的是第一个元素,使用 parallelStream() 并行时找到的是其中一个元素,返回的类型为Optional
1 | java复制代码public class FindOpTest { |
4.match
匹配操作,包含:allMatch、anyMatch、noneMatch
allMatch:所有元素都满足条件,返回boolean类型
anyMatch:任意一个元素满足条件,返回boolean类型
noneMatch:所有元素都不满足条件,返回boolean类型
1 | java复制代码public class MatchTest { |
5.count
统计操作,效果跟调用集合的size()方法类似
1 | java复制代码public class CountOpTest { |
6.min、max
min:获取最小值,返回Optional类型的数据
max:获取最大值,返回Optional类型的数据
1 | java复制代码public class MaxMinTest { |
7.reduce
规约操作,将整个数据流的值规约为一个值,count、min、max底层就是使用reduce。
reduce 操作可以实现从Stream中生成一个值,其生成的值不是随意的,而是根据指定的计算模型。
1 | java复制代码public class ReduceTest { |
8.toArray
数组操作,将数据流的元素转换成数组。
1 | java复制代码public class ArrayTest { |
stream和parallelStream的区别
stream:是单管道,称其为流,其主要用于集合的逻辑处理。
parallelStream:是多管道,提供了流的并行处理,它是Stream的另一重要特性,其底层使用Fork/Join框架实现
1 | java复制代码public class StreamTest { |
1 | java复制代码public class ParallelStreamTest { |
我们可以看到直接使用parallelStream的forEach遍历数据,是没有顺序的。
如果要让parallelStream遍历时有顺序怎么办呢?
1 | java复制代码public class ParallelStreamTest { |
parallelStream的工作原理:
实际工作中的案例
1.从两个集合中找相同的元素。一般用于批量数据导入的场景,先查询出数据,再批量新增或修改。
1 | java复制代码public class WorkTest { |
2.有两个集合a和b,过滤出集合a中有,但是集合b中没有的元素。这种情况可以使用在假如指定一个id集合,根据id集合从数据库中查询出数据集合,再根据id集合过滤出数据集合中不存在的id,这些id就是需要新增的。
1 | java复制代码@Test |
3.根据条件过滤数据,并且去重做数据转换
1 | java复制代码 @AllArgsConstructor |
4.统计指定集合中,姓名相同的人中年龄最小的年龄
1 | java复制代码@Test |
最后说一句(求关注,别白嫖我)
如果这篇文章对您有所帮助,或者有所启发的话,帮忙关注一下,您的支持是我坚持写作最大的动力。
求一键三连:点赞、转发、在看。
此外关注公众号:【苏三说技术】,在公众号中回复:面试、代码神器、开发手册、时间管理有超赞的粉丝福利,另外回复:加群,可以跟很多BAT大厂的前辈交流和学习。
本文转载自: 掘金