Java8 函数式编程stream流
1.初始环境准备
场景:现在有一个公司,公司部门有一级部门,二级部门甲和二级部门乙(其中二级部门甲和二级部门乙是一级部门的子部门),
一级部门下面有有001号员工小明,二级部门甲下面有002号员工小刚和003号员工小李,二级部门乙有002号员工小刚和004号员工小张,其中员工id是唯一的,员工小刚既是二级部门甲又是二级部门乙的员工。代码展示如下:
public class LambdaUseCase {
static List<Department> departmentList;
static {
// 一级部门,部门人员有001号员工小明
Department departmentOne = new Department("一级部门", 1,10000L,11000L,
Arrays.asList(new Person("001","小明",22)));
// 二级部门甲,部门人员有002号员工小刚和003号员工小李
Department departmentTwoFirst = new Department("二级部门甲", 2,8000L,13000L,
Arrays.asList(new Person("002","小刚",23),
new Person("003","小李",32)));
// 二级部门乙,部门人员有002号员工小刚和004号员工小张
Department departmentTwoSecond = new Department("二级部门已", 2,7500L,15000L,
Arrays.asList(new Person("002","小刚",23),
new Person("004","小张",34)));
departmentList = Arrays.asList(departmentOne,departmentTwoFirst,departmentTwoSecond);
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class Department {
// 部门名
private String departmentName;
// 部门等级
private Integer departmenRank;
// 部门薪资(单位分)
private Long departSalary;
// 部门日盈利
private Long departProfit;
// 部门人员集合
private List<Person> persons;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
//重写equal和hashcode方法,用于数据去重
@EqualsAndHashCode
class Person {
// 人员id
private String personId;
// 人员姓名
private String personName;
// 人员年龄
private Integer personAge;
}
2.创建流
单列集合: 集合对象.stream() 创建流
departmentList.stream();
数组:Arrays.stream(数组) 或者使用Stream.of来创建
Integer[] arr = {1,2,3,4,5};
Stream<Integer> stream = Arrays.stream(arr);
Stream<Integer> stream2 = Stream.of(arr);
Stream.of(1,2,3,4);
双列集合:转换成单列集合后再创建
Map<String, Integer> map = new HashMap<>();
map.put("a", 1);
map.put("b", 2);
map.put("c", 3);
Stream<Map.Entry<String, Integer>> stream = map.entrySet().stream();
3.中间操作
3.1 map
如果有一个函数可以将一种类型的值转换成另外一种类型,map 操作就可以使用该函数,将一个流中的值转换成一个新的流
需求:公司今年收益提供,决定把所有部门的平均薪资提升1000。
List<Department> departments = departmentList.stream().map(e -> {
e.setDepartSalary(e.getDepartSalary() + 1000);
return e;
}).collect(Collectors.toList());
3.2 filter
对流中的元素进行过滤,筛选出符合过滤条件的数据
需求:需要筛选出部门平均薪资大于等于8000的数据。
List<Department> departments = departmentList.stream()
.filter(e -> e.getDepartSalary() >= 8000)
.collect(Collectors.toList());
3.3 flatMap
flatMap 方法可用 Stream 替换值,然后将多个 Stream 连接成一个 Stream
需求:把公司所有的人员收集到一个集合中。
不使用flatMap,你可能会这么做,循环里套循环,看上去不太美观。
Set<Person> personSet = new HashSet<>();
departmentList.stream().forEach(e->{
e.getPersons().stream().forEach(person->{
personSet.add(person);
});
});
使用flatMap写法。
Set<Person> personSet = departmentList.stream()
.flatMap(e -> e.getPersons().stream())
.collect(Collectors.toSet());
3.4 distinct
去除流中的重复元素
需求:将公司所有的人员统计出来,这回不使用Set去重,注意:002号员工即在二级部门甲又在二级部门乙
List<Person> personSet = departmentList.stream()
.flatMap(e -> e.getPersons().stream())
.distinct()
.collect(Collectors.toList());
注意:distinct方法是依赖Object的equals方法来判断是否是相同对象的。所以需要注意重写equals方法。
3.5 sorted
对流中的元素进行排序
需求: 按薪资从低到高将部门列出来。
List<Department> departments = departmentList.stream()
.sorted((o1, o2) -> o1.getDepartSalary().compareTo(o2.getDepartSalary()))
.collect(Collectors.toList());
注意:如果调用空参的sorted()方法,需要流中的元素是实现了Comparable。
3.6 limit
可以设置流的最大长度,超出的部分将被抛弃
需求:按薪资从低到高将部门列出来,并且找出薪资最低的俩个部门
List<Department> departments = departmentList.stream()
.sorted(Comparator.comparing(Department::getDepartSalary))
.limit(2)
.collect(Collectors.toList());
3.7 skip
跳过流中的前n个元素,返回剩下的元素
需求:按薪资从低到高将部门列出来,并且忽略薪资最低的部门
List<Department> departments = departmentList.stream()
.sorted(Comparator.comparing(Department::getDepartSalary))
.skip(1)
.collect(Collectors.toList());
4.终结操作
4.1 foreach
对流中的元素进行遍历操作
需求:要求输出全部部门的名称
departmentList.stream()
.forEach(e -> System.out.println(e.getDepartmentName()));
4.2 count
可以用来获取当前流中元素的个数。
需求:统计部门的格式
long count = departmentList.stream()
.count();
4.3 max和min
Stream 上常用的操作之一是求最大值和最小值
需求:分别找出部门里面薪资最高的部门和最低的部门
Department maxDepartment = departmentList.stream()
.max(Comparator.comparing(Department::getDepartSalary))
.get();
Department minDepartment = departmentList.stream()
.min(Comparator.comparing(Department::getDepartSalary))
.get();
4.4 collect
将当前的流转换为一个集合
4.4.1 Collectors.toList()
需求:获得一个保存所有部门名字的集合
List<String> departNameList = departmentList.stream()
.map(Department::getDepartmentName)
.collect(Collectors.toList());
4.4.2 Collectors.toSet()
需求:获得部门所有人的姓名(重名的忽略)
Set<String> personNameList = departmentList.stream()
.flatMap(e -> e.getPersons().stream())
.map(Person::getPersonName)
.collect(Collectors.toSet());
4.4.3 Collectors.toMap(keyMapper, valueMapper)
需求:获得部门名称当做key,员工当做value的Map
Map<String, List<Person>> collect = departmentList.stream()
.collect(Collectors.toMap(Department::getDepartmentName, Department::getPersons));
4.4.4 Collectors.joining()
需求:将所有的部门名字连起来,并且前缀是【,后缀是】,分隔符是,
departmentList.stream()
.map(Department::getDepartmentName)
.collect(Collectors.joining(",","[","]"));
4.4.5 Collectors.partitioningBy(predicate)
接受一个流,并将其分成两部分,使用 Predicate 对象判断一个元素应该属于哪个部分,并根据布尔值返回一个 Map 到列表。
需求:将一级部门和其他级别的部门分离出来
Map<Boolean, List<Department>> collect = departmentList
.stream()
.collect(Collectors.partitioningBy(e -> e.getDepartmenRank() == 1));
4.4.6 Collectors.groupingBy(classifier)
接受一个分类函数,用来对数据分组
需求:将部门按部门的阶级分组
Map<Integer, List<Department>> collect = departmentList
.stream()
.collect(Collectors.groupingBy(Department::getDepartmenRank));
4.4.7 组合收集器
各种收集器已经很强大了,但如果将它们组合起来,会变得更强大。
需求:统计各个阶级部门的平均薪资
Map<Integer, Double> collect = departmentList.stream()
.collect(Collectors.groupingBy(Department::getDepartmenRank,Collectors.averagingLong(Department::getDepartSalary)));
这个例子中用到了第二个收集器,用以收集最终结果的一个子集。这些收集器叫作下游收集器。收集器是生成最终结果的一剂配方,下游收集器则是生成部分结果的配方,主收集器中会用到下游收集器。这种组合使用收集器的方式,使得它们在 Stream 类库中的作用更加强大。
4.5 reduce
对流中的数据按照指定的方式计算出结果
需求:计算出部门总的日盈利
Long sum = departmentList.stream()
.map(Department::getDepartProfit)
.reduce(0L, (result, element) -> result + element);
4.6 anyMatch
可以用来判断是否有任意符合匹配条件的元素,结果为boolean类型。
需求:判断部门中有没有薪资大于9000的部门
boolean b = departmentList.stream()
.anyMatch(e -> e.getDepartSalary() > 9000L);
4.7 allMatch
可以用来判断是否都符合匹配条件,结果为boolean类型。如果都符合结果为true,否则结果为false。
需求:判断部门薪资是不是都大于9000
boolean b = departmentList.stream()
.allMatch(e -> e.getDepartSalary() > 9000L);
4.8 noneMatch
可以判断流中的元素是否都不符合匹配条件。如果都不符合结果为true,否则结果为false
需求:判断是不是所有的部门薪资都大于9000
boolean b = departmentList.stream()
.noneMatch(e -> e.getDepartSalary() > 9000L);
4.9 findAny
获取流中的任意一个元素。该方法没有办法保证获取的一定是流中的第一个元素。
需求:找到任意一个部门
Department department = departmentList
.stream()
.findAny()
.get();
4.10 findFirst
获取流中的第一个元素。
需求:获得薪资最低的一个部门
Department department = departmentList
.stream()
.sorted(Comparator.comparing(Department::getDepartSalary))
.findFirst()
.get();
5.高级用法
5.1 对基本类型处理(mapToInt,mapToLong,mapToDouble)
在 Java 中,有一些相伴的类型,比如 int 和 Integer——前者是基本类型,后者是装箱类型。基本类型内建在语言和运行环境中,是基本的程序构建模块;而装箱类型属于普通的 Java 类,只不过是对基本类型的一种封装。由于装箱类型是对象,因此在内存中存在额外开销。比如,整型在内存中占用4 字节,整型对象却要占用 16 字节。这一情况在数组上更加严重,整型数组中的每个元素只占用基本类型的内存,而整型对象数组中,每个元素都是内存中的一个指针,指向 Java堆中的某个对象。在最坏的情况下,同样大小的数组,Integer[] 要比 int[] 多占用 6 倍内存。
将基本类型转换为装箱类型,称为装箱,反之则称为拆箱,两者都需要额外的计算开销。对于需要大量数值运算的算法来说,装箱和拆箱的计算开销,以及装箱类型占用的额外内存,会明显减缓程序的运行速度。
为了减小这些性能开销,Stream 类的某些方法对基本类型和装箱类型做了区分。高阶函数 mapToLong 和其他类似函数即为该方面的一个尝试。在 Java 8 中,仅对整型、长整型和双浮点型做了特殊处理,因为它们在数值计算中用得最多,特殊处理后的系统性能提升效果最明显。
如有可能,应尽可能多地使用对基本类型做过特殊处理的方法,进而改善性能。这些特殊的 Stream 还提供额外的方法,避免重复实现一些通用的方法,让代码更能体现出数值计算的意图。
需求:需要计算出公司部门利润的平均值,最大值,最小值,以及利润总和。
LongSummaryStatistics longSummaryStatistics = departmentList
.stream()
.mapToLong(Department::getDepartProfit)
.summaryStatistics();
System.out.println("利润最大值"+longSummaryStatistics.getMax());
System.out.println("利润最小值"+longSummaryStatistics.getMin());
System.out.println("利润平均值"+longSummaryStatistics.getAverage());
System.out.println("利润总和"+longSummaryStatistics.getSum());
这些统计值在所有特殊处理的 Stream,如 DoubleStream、LongStream 中都可以得出。如无需全部的统计值,也可分别调用 min、max、average 或 sum 方法获得单个的统计值,同样,三种基本类型对应的特殊 Stream 也都包含这些方法。
6. 数据并行化
并 行 化 操 作 流 只 需 改 变 一 个 方 法 调 用。 如 果 已 经 有 一 个 Stream 对 象, 调 用 它 的parallel 方法就能让其拥有并行操作的能力。如果想从一个集合类创建一个流,调用parallelStream 就能立即获得一个拥有并行能力的流。
需求:并行的统计公司的所有的人员。
Set<Person> collect = departmentList.stream()
.parallel()
.flatMap(e -> e.getPersons().stream()).collect(Collectors.toSet());
Set<Person> collect1 = departmentList.parallelStream()
.flatMap(e -> e.getPersons().stream()).collect(Collectors.toSet());
在底层,并行流还是沿用了 fork/join 框架。fork 递归式地分解问题,然后每段并行执行,最终由 join 合并结果,返回最后的值。
Java8 函数式编程stream流的更多相关文章
- 13函数式编程&Stream流
13.1常用的函数式接口总结 接口名称 方法名称 抽象/默认 延迟/终结 方法描述 Supplier get 抽象 终结 供给型接口,无参有返回值,主要用于 Consumer accept 抽象 ...
- [三]java8 函数式编程Stream 概念深入理解 Stream 运行原理 Stream设计思路
Stream的概念定义 官方文档是永远的圣经~ 表格内容来自https://docs.oracle.com/javase/8/docs/api/ Package java.util.s ...
- [一] java8 函数式编程入门 什么是函数式编程 函数接口概念 流和收集器基本概念
本文是针对于java8引入函数式编程概念以及stream流相关的一些简单介绍 什么是函数式编程? java程序员第一反应可能会理解成类的成员方法一类的东西 此处并不是这个含义,更接近是数学上的 ...
- Java8函数式编程探秘
引子 将行为作为数据传递 怎样在一行代码里同时计算一个列表的和.最大值.最小值.平均值.元素个数.奇偶分组.指数.排序呢? 答案是思维反转!将行为作为数据传递. 文艺青年的代码如下所示: public ...
- [2017.02.23] Java8 函数式编程
以前学过Haskell,前几天又复习了其中的部分内容. 函数式编程与命令式编程有着不一样的地方,函数式编程中函数是第一等公民,通过使用少量的几个数据结构如list.map.set,以及在这些数据结构上 ...
- 关于Java8函数式编程你需要了解的几点
函数式编程与面向对象的设计方法在思路和手段上都各有千秋,在这里,我将简要介绍一下函数式编程与面向对象相比的一些特点和差异. 函数作为一等公民 在理解函数作为一等公民这句话时,让我们先来看一下一种非常常 ...
- Java8 函数式编程详解
Java8 函数式编程详解 Author:Dorae Date:2017年11月1日23:03:26 转载请注明出处 说起Java8,可能很多人都已经知道其最大的改进,就是引入了Lambda表达式与S ...
- 第46天学习打卡(四大函数式接口 Stream流式计算 ForkJoin 异步回调 JMM Volatile)
小结与扩展 池的最大的大小如何去设置! 了解:IO密集型,CPU密集型:(调优) //1.CPU密集型 几核就是几个线程 可以保持效率最高 //2.IO密集型判断你的程序中十分耗IO的线程,只要大于 ...
- Java8中的Stream流式操作 - 入门篇
作者:汤圆 个人博客:javalover.cc 前言 之前总是朋友朋友的叫,感觉有套近乎的嫌疑,所以后面还是给大家改个称呼吧 因为大家是来看东西的,所以暂且叫做官人吧(灵感来自于民间流传的四大名著之一 ...
- 【Java8新特性】面试官问我:Java8中创建Stream流有哪几种方式?
写在前面 先说点题外话:不少读者工作几年后,仍然在使用Java7之前版本的方法,对于Java8版本的新特性,甚至是Java7的新特性几乎没有接触过.真心想对这些读者说:你真的需要了解下Java8甚至以 ...
随机推荐
- Windows防病毒Defender 排除病毒误报
开发的软件安装后,windows上提示病毒,默默被系统删除了. 一开始以为是自己软件的签名问题,后面发现,将被隔离的文件还原,文件的签名是存在的. 这是微软denfender的误报,为啥会报病毒呢? ...
- C# 反射 判断类型是否是列表
1 /// <summary> 2 /// 判断类型是否为可操作的列表类型 3 /// </summary> 4 /// <param name="type&q ...
- [ [Ynoi2013] 无力回天 NOI2017 ] 解题报告
[Ynoi2013] 无力回天 NOI2017 首先看到异或,想到能维护异或的东西就那几样(线性基/01trie/数位 dp/FWT),再看到求选任意个数后的异或最大值,线性基无疑了. 这时再看还要维 ...
- MySQL事务和锁实战篇
文章目录 MySQL事务和锁 事务 事务的控制语句 事务隔离级别设置 脏读 不可重复读 幻读 锁机制 InnoDB的行级锁 锁实战 死锁 总结 MySQL事务和锁 事务 说到关系型的数据库的事务,相信 ...
- define定义常量和宏
define:预处理指令 使用方法有两种 1.define定义符号 denfine定义常量 2.define定义宏 宏是有参数的,它的参数是替换 常规来说这样写define定义宏没啥问题 但是这样写 ...
- 2021-03-07:在一个数组中,对于每个数num,求有多少个后面的数 * 2 依然<num,求总个数。比如:[3,1,7,0,2],3的后面有:1,0;1的后面有:0;7的后面有:0,2;0的后面没有;2的后面没有;所以总共有5个。
2021-03-07:在一个数组中,对于每个数num,求有多少个后面的数 * 2 依然<num,求总个数.比如:[3,1,7,0,2],3的后面有:1,0:1的后面有:0:7的后面有:0,2:0 ...
- HINT: Add or change a related_name argument to the definition for 'usersApp.
错误原因是你的项目使用的不是Django自带的用户表,采用的自定义的用户表,这个时候需要在settings.py里面进行指定. AUTH_USER_MODEL = 'usersApp.UserProf ...
- 【GiraKoo】Could NOT find PkgConfig (missing: PKG_CONFIG_EXECUTABLE)
[解决]Could NOT find PkgConfig (missing PKG_CONFIG_EXECUTABLE) 环境 Ubuntu 22.04 现象 在编写CMakeLists.txt时,调 ...
- sipp重放rtp数据测试FreeSWITCH
环境:CentOS 7.6_x64 FreeSWITCH版本 :1.10.9 sipp版本:3.6.1 一.背景描述 sipp是一款VoIP测试工具,日常开发过程中会使用到该软件,但其自身携带的pca ...
- linux PXE和无人值守
目录 一.pxe概念 二.pxe相关服务 三.pxe装机流程 四.pxe四大文件 五.无人值守 六.实验 自动装机 一.pxe概念 概念:PXE(预启动执行环境)是由Intel公司开发的网络引导技术, ...