函数式编程(JAVA)——Stream流
概述
Java8的Stream使用的是函数式编程模式,如同它的名字一样,它可以被用来对集合或数组进行链状流式的操作。可以更方便的让我们对集合或数组操作。
下述所有测试代码源码地址:https://gitee.com/ArnoldSu/functionalProgramming.git
初始化之后所有演示代码需要用到的数据集合。
public static User createUser() {
return new User()
.setId(RandomUtil.randomLong())
.setAge(RandomUtil.randomInt(0, 100))
.setName(RandomUtil.randomString("澄邈德泽海超海阳海荣海逸海昌瀚钰瀚文涵亮涵煦明宇涵衍浩皛浩波浩博浩初浩宕浩歌浩广浩邈浩气浩思浩言鸿宝鸿波鸿博鸿才鸿畅鸿畴鸿达鸿德鸿飞鸿风鸿福鸿光鸿晖鸿朗鸿文鸿轩鸿煊鸿骞鸿远鸿云鸿哲鸿祯鸿志鸿卓嘉澍光济澎湃彭泽鹏池鹏海浦和浦泽瑞渊越泽博耘德运辰宇辰皓辰钊辰铭辰锟辰阳辰韦辰良辰沛晨轩晨涛晨濡晨潍鸿振吉星铭晨起运运凡运凯运鹏运浩运诚运良运鸿运锋运盛运升运杰运珧运骏运凯运乾维运运晟运莱运华耘豪星爵星腾星睿星泽星鹏星然震轩震博康震震博振强振博振华振锐振凯振海振国振平昂然昂雄昂杰昂熙昌勋昌盛昌淼昌茂昌黎昌燎昌翰晨朗德明德昌德曜范明飞昂高旻晗日昊然昊天昊苍昊英昊宇昊嘉昊明昊伟昊硕昊磊昊东鸿晖鸿朗华晖金鹏晋鹏敬曦景明景天景浩俊晖君昊昆琦昆鹏昆纬昆宇昆锐昆卉昆峰昆颉昆谊昆皓昆鹏昆明昆杰昆雄昆纶鹏涛鹏煊曦晨曦之新曦旭彬旭尧旭鹏旭东旭炎炫明宣朗学智轩昂彦昌曜坤曜栋曜文曜曦曜灿曜瑞智伟智杰智刚智阳昌勋昌盛昌茂昌黎昌燎昌翰晨朗昂然昂雄昂杰昂熙范明飞昂高朗高旻德明德昌德曜智伟智杰智刚智阳瀚彭旭炎宣朗学智昊然昊天昊苍昊英昊宇昊嘉昊明昊伟鸿朗华晖金鹏晋鹏敬曦景明景天景浩景行景中景逸景彰昆鹏昆明昆杰昆雄昆纶鹏涛鹏煊景平俊晖君昊昆琦昆鹏昆纬昆宇昆锐昆卉昆峰昆颉昆谊轩昂彦昌曜坤曜文曜曦曜灿曜瑞曦晨曦之新曦鑫鹏旭彬旭尧旭鹏旭东浩轩浩瀚浩慨浩阔鸿熙鸿羲鸿禧鸿信泽洋泽雨哲瀚胤运佑运允晨运恒运发云天耘志耘涛振荣振翱中震子辰晗昱瀚玥瀚昂瀚彭景行景中景逸景彰绍晖文景曦哲永昌子昂智宇智晖晗日晗昱瀚昂昊硕昊磊昊东鸿晖绍晖文昂文景曦哲永昌子昂智宇智晖浩然鸿运辰龙运珹振宇高朗景平鑫鹏昌淼炫明昆皓曜栋文昂治汇", 2))
.setNickname(RandomUtil.randomString("风雨踏梦行 樱花味女孩 柠檬泪 日之夕矣 半世浮生 逆水寒 雨后初晴 陌路 不缺友人 听风说旧人 舟不渡我 哑剧 沫丶白色控 傻子快来~ 厌己 森屿海巷 断桥再见 坚毅之梦 щǒ冇寎 遗忘 梦想的翅膀 捂着心脏说胃疼 南巷孤猫i 手捧阳光 樱花涵 指尖微凉° 你给的承诺丶早已泛黄 尐懒蟲ゞ 坚强的另一个名字叫苦撑 孤街浪人 瞌睡虫 薄荷凉 孤魄 热巴 温柔似你眉眼 离心咒ゝ 那一抹浅笑隱藏忧伤 盛夏北梦初心未改 幸福式梦想※ 唯念 依然血红。 沦陷的痛", 5))
.setSex(RandomUtil.randomInt(0, 2))
.setDep(createDepartment());
}
public static List<Department> createDepartment() {
List<Department> departments = new ArrayList<Department>();
int i = RandomUtil.randomInt(1, 4);
for (int j = 0; j < i; j++) {
departments.add(new Department().setId(RandomUtil.randomLong())
.setName(RandomUtil.randomString("人事技术前台前端后端", 2))
.setCode(RandomUtil.randomInt(1, 10000))
.setParentId(RandomUtil.randomLong(1, 10000))
);
}
return departments;
}
public static List<User> initUsers = new ArrayList<User>();
@BeforeAll
public static void init() {
for (int i = 0; i < 10; i++) {
initUsers.add(createUser());
}
}
常用操作
filter
可以对流中的元素进行条件过滤,符合过滤条件的才能继续留在流中。
eg:过滤用户年龄大于80岁的人并且打印。
/**
* description Stream filter 对流中的参数进行条件过滤(过滤用户年龄大于80岁的人并且打印)
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void filterTest() {
initUsers.stream().filter(user -> user.getAge() > 80).forEach(user -> System.out.println(user.toString()));
}
map
可以把对流中的元素进行计算或转换。
eg: 通过map获取用户的姓名并且遍历打印。
/**
* description Stream map 对流中的参数进行计算和转换(通过map获取用户的姓名并且遍历打印)
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void mapTest() {
initUsers.stream().map(User::getName).forEach(userName -> System.out.println(userName));
}
distinct
可以去除流中的重复元素。
eg:通过map获取用户的姓名去重并且遍历打印。
/**
* description Stream distinct 对流中的参数进行去重(通过map获取用户的姓名去重并且遍历打印)
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void distinctTest() {
List<User> users = new ArrayList<>();
users.add(new User().setName("重复名称").setAge(1));
users.add(new User().setName("重复名称").setAge(2));
users.stream().map(User::getName).distinct().forEach(userName -> System.out.println(userName));
}
注意:distinct方法是依赖Object的equals方法来判断是否是相同对象的。所以需要注意重写equals方法。
sorted
可以对流中的元素进行排序。
eg: 通过用户的年龄升序排序并且遍历打印。
/**
* description Stream sorted 对流中的参数进行排序(通过用户的年龄升序排序并且遍历打印)
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void sortedTest() {
initUsers.stream().sorted(Comparator.comparingInt(User::getAge)).forEach(user -> System.out.println(user));
}/**
* description Stream limit 设置流的最大长度,超出的部分将被抛弃(获取三个用户信息并且遍历打印)
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void limitTest() {
initUsers.stream().limit(3).forEach(user -> System.out.println(user));
}
注意:如果调用空参的sorted()方法,需要流中的元素是实现了Comparable。
limit
可以设置流的最大长度,超出的部分将被抛弃。
eg:获取三个用户信息并且遍历打印。
/**
* description Stream limit 设置流的最大长度,超出的部分将被抛弃(获取三个用户信息并且遍历打印)
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void limitTest() {
initUsers.stream().limit(3).forEach(user -> System.out.println(user));
}
skip
跳过流中的前n个元素,返回剩下的元素。
eg:跳过前五个用户输出剩下的用户信息并且遍历打印。
/**
* description Stream skip 跳过流中的前n个元素,返回剩下的元素。(跳过前五个用户输出剩下的用户信息并且遍历打印)
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void skipTest() {
initUsers.stream().skip(5).forEach(user -> System.out.println(user));
}
flatMap
map只能把一个对象转换成另一个对象来作为流中的元素。而flatMap可以把一个对象转换成多个对象作为流中的元素。
eg:输出用户下的部门信息并且遍历打印。
/**
* description Stream flatMap map只能把一个对象转换成另一个对象来作为流中的元素。
* 而flatMap可以把一个对象转换成多个对象作为流中的元素。(输出用户下的部门信息并且遍历打印)
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void flatMapTest() {
initUsers.stream().flatMap(user -> user.getDep().stream()).forEach(department -> System.out.println(department));
}
count
可以用来获取当前流中元素的个数。
eg:获取stream中的元素个数并且打印。
/**
* description Stream count 可以用来获取当前流中元素的个数。(获取stream中的元素个数并且打印)
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void countTest() {
System.out.println(initUsers.stream().count());
}
max&min
可以用来或者流中的最值。
eg:获取用户年龄最大的人/最小年龄人。
/**
* description Stream max/min 可以用来获取当前流中元素最值大和最小。(获取用户年龄最大的人/最小年龄人)
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void maxOrMinTest() {
Optional maxOptional = initUsers.stream().max(Comparator.comparing(User::getAge));
System.out.println(maxOptional.get());
Optional minOptional = initUsers.stream().min(Comparator.comparing(User::getAge));
System.out.println(minOptional.get());
}
collect
collect基础用法
把当前流转换成一个集合。
eg:获取用户的昵称转集合并且遍历输出打印。
/**
* description Stream collect 把当前流转换成一个集合。(获取用户的昵称转集合并且遍历输出打印)
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void collectTest() {
initUsers.stream().map(User::getNickName).collect(Collectors.toList()).stream().forEach(nickName -> System.out.println(nickName));
}
List 转 Map的常用四种用法
//List 转 Map的常用四种用法:
//第⼀种: 取list中某2个字段作为Map的K,V
Map<String, Integer> map1 = initUsers.stream().collect(Collectors.toMap(User::getNickName, User::getAge));
map1.forEach((key, value) -> System.out.println(key + "=====" + value));
//第⼆种:将id和实体Bean做为K,V
Map<String, User> map2 = initUsers.stream().collect(Collectors.toMap(User::getNickName, user -> user));
map2.forEach((key, value) -> System.out.println(key + "=====" + value));
//第三种: key存在重复记录时处理
Map<String, User> map3 = initUsers.stream().collect(Collectors.toMap(User::getNickName, Function.identity(), (key1, key2) -> key2));
map3.forEach((key, value) -> System.out.println(key + "=====" + value));
//第四种: 使⽤某个具体的Map类来保存,如保存时使⽤LinkedHashMap
Map<String, User> map4 = initUsers.stream().collect(Collectors.toMap(User::getNickName, Function.identity(), (key1, key2) -> key2, LinkedHashMap::new));
map4.forEach((key, value) -> System.out.println(key + "=====" + value));
anyMatch
用来判断是否有任意符合匹配条件的元素,结果为boolean类型。
eg:判断是否有用户年龄大于90岁人员。
/**
* description Stream anyMatch 用来判断是否有任意符合匹配条件的元素,结果为boolean类型。(判断是否有用户年龄大于90岁人员)
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void anyMatchTest() {
System.out.println(initUsers.stream().anyMatch(user -> user.getAge() > 90));
}
allMatch
用来判断是否都符合匹配条件,结果为boolean类型。如果都符合结果为true,否则结果为false。
eg:判断是否所有用户年龄大于60岁人员。
/**
* description Stream allMatch 可以用来判断是否都符合匹配条件,结果为boolean类型。如果都符合结果为true,否则结果为false。(判断是否所有用户年龄大于60岁人员)
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void allMatchTest() {
System.out.println(initUsers.stream().allMatch(user -> user.getAge() > 60));
}
noneMatch
判断流中的元素是否都不符合匹配条件。如果都不符合结果为true,否则结果为false
eg:判断是否所有用户年龄都不大于98岁人员。
/**
* description Stream noneMatch 判断流中的元素是否都不符合匹配条件。如果都不符合结果为true,否则结果为false。(判断是否所有用户年龄都不大于98岁人员)
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void noneMatchTest() {
System.out.println(initUsers.stream().noneMatch(user -> user.getAge() > 98));
}
findAny
获取流中的任意一个元素。
eg:随机获取一个人员信息并且打印。
/**
* description Stream findAny 获取流中的任意一个元素。(随机获取一个人员信息并且打印)
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void findAnyTest() {
System.out.println(initUsers.stream().findAny());
}
findFirst
获取流中的第一个元素。
eg:获取人员中岁数最小的一个人员信息。
/**
* description Stream findFirst 获取流中的第一个元素。(获取人员中岁数最小的一个人员信息)
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void findFirstTest() {
System.out.println(initUsers.stream().sorted(Comparator.comparing(User::getAge)).findFirst().get());
}
reduce
对流中的数据按照你指定的计算方式计算出一个结果。
reduce的作用是把stream中的元素给组合起来,我们可以传入一个初始值,它会按照我们的计算方式依次拿流中的元素和初始化值进行计算,计算结果再和后面的元素计算。
eg:计算所有用户的年龄之和。
/**
* description Stream reduce 对流中的数据按照你指定的计算方式计算出一个结果。(计算所有用户的年龄之和)
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void reduceTest() {
System.out.println(initUsers.stream().mapToInt(User::getAge).reduce(0, Integer::sum));
}
注意事项
- 流是一次性的(一旦一个流对象经过一个终结操作后。这个流就不能再被使用)
- 不会影响原数据(我们在流中可以多数据做很多处理。但是正常情况下是不会影响原来集合中的元素的。这往往也是我们期望的)