前言
在日常使用集合时,我们通常使用迭代器来处理集合中的数据,假如有一个用户列表 List
本篇博客是上篇博客关于 Stream 流介绍的延续。如果读者对 Stream 流,函数式编程不了解。可以先读我的前几篇博客
声明:本文首发于博客园,作者:后青春期的Keats;地址:https://www.cnblogs.com/keatsCoder/ 转载请注明,谢谢!
收集器
先看看用收集器实现的分组 List
List<User> userList = new ArrayList<>();
User user1 = new User(1,"张三","beijing");
User user2 = new User(1,"李四","xi'an");
User user3 = new User(1,"Keats","xi'an");
userList.add(user1);
userList.add(user2);
userList.add(user3);
Map<String, List<User>> result = userList.stream().collect(Collectors.groupingBy(User::getLocation));
Set<String> keySet = result.keySet();
keySet.forEach(key -> System.out.println(result.get(key)));
控制台输出
[User{sex=1, name='李四', location='xi'an'}, User{sex=1, name='Keats', location='xi'an'}]
[User{sex=1, name='张三', location='beijing'}]
看了之后,是不是感觉 Stream 真方便!接下来我就向大家介绍一下 Stream 收集器 collect 的用法。
将流元素规约/汇总为一个值
流元素计数
假如需要知道集合中来自西安的用户数量,可以使用 collect(Collectors.counting())
方法
Long collect = userList.stream().filter(u -> "xi'an".equals(u.getLocation())).collect(Collectors.counting());
也可以简写成:
Long collect = userList.stream().filter(u -> "xi'an".equals(u.getLocation())).count();
最大,最小值
如果需要知道用户列表中最大年龄的用户
Optional<User> maxAge = userList.stream().collect(Collectors.maxBy(Comparator.comparing(User::getAge)));
也可以简写成:
Optional<User> maxAge = userList.stream().max(Comparator.comparing(User::getAge));
相应的最小年龄的用户只需要使用 minBy() 或者 min() 方法就可以了
汇总
如果要知道用户列表中的年龄和
Integer sumAge = userList.stream().collect(Collectors.summingInt(User::getAge));
Integer sumAge = userList.stream().mapToInt(User::getAge).sum();
这里使用了 summingInt / mapToInt 方法,将返回值限定为 Integer 类型,其他包装类型也有对应的 summingXxx 方法。这里不再赘述
拼接字符串
如果要将所有的用户姓名拼接成一个字符串
String nameStr = userList.stream().map(User::getName).collect(Collectors.joining());
Collectors.joining()
该方法会默认采用 StringBuilder 方法将 Stream
另外该方法还提供了两个重载方法,可以自己设置分隔符 joining(CharSequence delimiter)
,或者设置分隔符,转换后字符串的前缀及后缀 joining(CharSequence delimiter, CharSequence prefix, CharSequence suffix)
元素分组
就像收集器开篇的代码一样,使用 Collectors.groupingBy()
方法可以快速的将流中元素分组。有的时候有些分组条件比较复杂,并不能通过方法引用直接分组。比如年龄小于等于 18 岁成为小孩,大于 18 岁成为大人。这时就需要自己写 Lambda 表达式来实现了
Map<String, List<User>> result = userList.stream().collect(Collectors.groupingBy(u -> {
if (u.getAge() <= 18) {
return "小孩";
} else {
return "大人";
}
}));
多级分组
如果想要年龄分组之后,根据位置分组的两重分组,可以这么实现:
Map<String, Map<Integer, List<User>>> result = userList.stream().collect(Collectors.groupingBy(u -> {
if (u.getAge() <= 18) {
return "小孩";
} else {
return "大人";
}
}, Collectors.groupingBy(User::getAge)));
groupingBy() 方法支持重载方法,groupingBy(Function<? super T, ? extends K> classifier, Collector<? super T, A, D> downstream)
这个方法第二个参数是一个新的收集器,你可以创建下级分组,可以计数/求最值/拼串等等。你还可以继续调用 groupingBy 实现更深层次的分组。这里我举一个计数的例子
Map<String, Long> result = userList.stream().collect(Collectors.groupingBy(u -> {
if (u.getAge() <= 18) {
return "小孩";
} else {
return "大人";
}
}, Collectors.counting()));
其他用法大家可以结合博客前面介绍的 API 自行发挥
元素分区
分区可以看成是分组的特殊情况,他所返回的 Map key 是 boolean 类型的。故它的分类函数是一个谓词函数(返回布尔值的函数)。例如我们将年龄大于18的设为true,可以有下面的分区方法:
Map<Boolean, List<User>> result = userList.stream().collect(Collectors.partitioningBy(u -> u.getAge() > 18));
总结
通过这两篇博客对 Stream 流的理解,相信大家和我一样感受到 Stream 流在处理数据时得天独厚的优势。希望大家在读完博客之后回过头看看自己项目中的一些处理集合的代码,是否可以使用流重构一下,在项目中使用它加深记忆。而不是看完就忘。去领略函数式编程的魅力吧!
码字不易,如果你觉得读完以后有收获,不妨点个推荐让更多的人看到吧!