Java函数式编程学习笔记(三)
前言
时隔三个月,我终于回归到技术博客写作中了。这段时间我经历了一个半月的应届生新员工培训,之后接续实习期间完成的工作,继续进行业务需求的开发。由于开发时实在分不出精力撰写文章,博客也被闲置了将近3个月,在此期间博客仅部署了两次平台框架版本的例行升级。
言归正传,最近在业务逻辑编写工作中用到了不少Stream
流和Lambda
函数。使用不同的Stream
操作、编写不同功能的Lambda
函数,能够输出各式各样的数据结构,从而满足业务逻辑的需求。于是写下这篇文章以作总结。这篇文章中的每个代码片段都是我在实际开发中编写的,希望能对各位有些帮助。
Talk is cheap, show me the code.
经典案例
1 | Set<Integer> alarmIdSet = realTimeData.entrySet().stream().filter(it -> Objects.nonNull(it.getValue()) && it.getValue() > 0) |
这段代码是Stream
流非常常见的使用案例,把常见的fliter
、map
、collect
方法都用到了,最终将一个Map
的EntrySet
转换成所需条件的KeySet
。
对
Stream
流不太了解的同学,可以查阅与本文同一专题的《Java函数式编程学习笔记(二)》。该篇文章较为详细地介绍了Stream
流的各种操作。
对象List转Map
1 | Map<Long, Integer> preInspectionMap = preInspectionList.stream() |
如果对Stream
有了基本的了解,那么这段代码就一目了然了,它使用Collectors.toMap
方法取了一个POJO
对象的两个属性分别作为Map
的Key
与Value
,最终将对象List
转换为Map
。
值得一提的是,当Collectors.toMap
方法有三个入参时,除了前两个入参表示Key
和Value
的映射方法外,第三个入参需传入一个Lambda
,用于定义当key
发生冲突时的处理方式。(v1, v2) -> v2
表示如果新插入的键值对的Key
已存在,就用这个Key
的新Value
替换旧Value
。下面这段代码就符合这种场景。
1 | return apiResult.getData().stream() |
对
Lambda
不太了解的同学,可以查阅与本文同一专题的《Java函数式编程学习笔记(一)》。该篇文章简短地介绍了Java函数式编程的概念和Java 8的Lambda
特性。
分组
Stream拥有强大的分组输出操作,使用Collectors.groupingBy
方法传入不同Lambda
即可实现不同功能的归类。这里归纳了三种,分别是归类为对象List、归类并统计数量、归类为对象属性List。
归类为对象List
1 | Map<Long, List<SystemEventStatisticsVo>> eventGroups = systemEventStatisticsVos.stream() |
这是最基本的归类功能,只需在groupingBy
方法中传入POJO对象的一个属性的Getter
方法引用,就能按这个属性分组输出Map
,此时Map
的Value
数据类型为对象List
。
归类并统计数量
基本案例
1 | Map<Long, Long> preInspectionEventMap = eventList.stream(). |
将方法引用替换为
Lambda
1 | Map<String, Long> hiddenDangerMap = hiddenDangerAccountList.stream() |
稍微复杂一点的
Lambda
,内容是条件运算
1 | Map<Integer, Long> enterpriseGroups = enterpriseList.stream().collect( |
这三段代码都在groupingBy
方法中传入了一个Collectors.counting
,用于在分组的同时统计每组元素的数量。
归类为对象属性List
1 | Map<Long, List<Long>> roomPreInspectionMap = preInspectionList.stream() |
这段代码在groupingBy
方法中传入了一个Collectors.mapping
,将分组的功能改为输出对象的属性List
,相当于在归类为对象List
后将对象List
额外做了一次Stream
流的Map
操作。
统计数量
Java中统计数量的逻辑可以使用for
循环遍历集合来实现,也可以使用Stream
的reduce
操作来实现。
使用merge
1 | resultMap.merge(EquipmentTypeEnum.of(preInspectionTypeMap.get(it.getObjectId())), 1L, Long::sum); |
上面两段代码都是将Map
中一个Key
对应的Value
作为计数器,用merge
方法实现了累加功能,从而达到统计数量的效果。merge
方法的三个入参分别是Key
的取值;每次取到相同Key
时累加的值;实现累加的Lambda
或方法引用。
此处merge
方法的含义为:每次取到相同的Key
时,在这个Key
对应的Value
基础上加1
。事实上merge
不止有累加的功能,更多功能还等待着我们去发掘。
使用reduce
1 | Integer eventCount = eventList.stream().map(SystemEventStatisticsVo::getCount) |
前面说到,reduce
方法是Stream
流的一种聚合操作,能将所有元素聚合成一个结果。这里是取了POJO
对象的Count
属性并做累加操作,得到所有元素Count
属性的总和。
1 | long eventCount = roomPreInspectionList.stream().reduce(0L, (total, it) -> { |
这段代码将一段Lambda
作为聚合方法,按聚合函数的逻辑输出一个结果。
使用Optional
1 | Optional<TrendDataVo> first = data.stream().filter(d -> paramsIsEquals(d, l)).findFirst(); |
这段代码中,Optional
的作用是在无法确定一个Stream
的元素是否存在时定义的一种数据结构。当元素存在时,才执行ifPresent
方法中的逻辑,是处理NPE或数组越界异常时替代if
语句的更优雅的一种方法。
非常感谢你的阅读,辛苦了!
参考文章: (感谢以下资料提供的帮助)