基于Java8 Lambda表达式,Stream可以便捷高效的对集合对象进行转换,筛选,排序,聚合等操作,极大的提高编程效率和程序可读性。同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势。
流的操作类型分为两种:
中间操作(Intermediate Operations):一个流可以后面跟随零个或多个 intermediate 操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),多个Intermediate操作只会在 Terminal 操作的时候融合起来,一次循环完成,就是说,仅仅调用到这类方法,并没有真正开始流的遍历。
这类操作有:
map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered
终止操作(Terminal Operations):一个流只能有一个 terminal 操作,最终会从Stream中得到值。
这类操作有:
forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator
可以这样判断这两种操作:如果返回值是Stream,则该操作是中间操作,如果返回值是其他值或者为空,则该操作是终止操作。
另外当返回的是无限大的Stream, 但希望在有限时间内返回有限的Stream。可以用
anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 limit这些,这些操作也叫做short-circuiting。
构造流的几种常见方法
1.Stream.of("a", "b", "c");
2.Stream.of(new String[] {"a", "b", "c"});
3.List<String> list = Arrays.asList(strArray);
list.stream();
对基本类型,还有IntStream、LongStream、DoubleStream。一般情况,上面三种方法够用了。
快速构建数值流
1、IntStream.range(1, 3).forEach(System.out::println)
输出1,2,类似[1, 3)左闭右开。
2、IntStream.rangeClosed(1,3).forEach(System.out::println)
输出1,2,3,类似[1, 3]。
流转换为其它数据结构
转为数组:
String[] arr = Stream.of("a").toArray(String[]::new);
转为List:
List<String> list1 = Stream.of("a").collect(Collectors.toList())
List<String> list2 = Stream.of("a").collect(Collectors.toCollection(ArrayList::new));
转为set
Set set1 = Stream.of("a").collect(Collectors.toSet());
转为string,逗号分隔
String str = Stream.of("a").collect(Collectors.joining(“,”)).toString();
List转换为Map,使用Collectors.toMap方法进行转换
// 值为对象的属性
Map<Integer,String> userMap1 = userList.stream().collect(Collectors.toMap(User::getId,User::getName));
// 值为对象
Map<Integer,User> userMap2 = userList.stream().collect(Collectors.toMap(User::getId,User->User));
// 值为对象另一种写法,Function.identity(),返回对象本身
Map<Integer,User> userMap3 = userList.stream().collect(Collectors.toMap(User::getId, Function.identity()));
// 但如果集合对象有重复的key,会报错key冲突错误,解决办法是选择第二个key覆盖第一个key。
Map<Integer,User> userMap4 = userList.stream().collect(Collectors.toMap(User::getId, Function.identity(),(key1,key2)->key2));
案例
下面以User为例
public class User {
private Integer id;
private String name;
private Integer age;
}
List <User> userList = new ArrayList<>();
userList.add(new User(1, "zhang3", 25));
userList.add(new User(1, "li4", 26));
userList.add(new User(1, "wang5", 25));
MAP用法:将流中的每一个元素映射为另外的元素
userList.stream.map(User::getName).collect(Collectors.toList());
userList.stream.map(u-> u.getName()).collect(Collectors.toList());
但有时使用map()操作后,返回的不是一个值,而是一个集合或者一个数组的时候,这时候想要获取这些集合或数组中的值,就可以使用flatMap解决这个问题。
List<User> users = Stream.of(userList).flatMap(list -> list.stream()).collect(Collectors.toList());
filter(): 返回结果生成新的流中只包含满足筛选条件的数据
筛选大于25岁,且叫“zhao6”的人
userList.stream().filter(u -> (u.getAge() > 25 &&"zhao6".equals(u.getName())))
.collect(Collectors.toList())
distinct():去重
对年龄去重
userList.stream().map(User::getAge).distinct().collect(Collectors.toList());
limit(n):截取流前n个元素
userList.stream().map(User::getAge).limit(1);//截取第一位
skip(n): 跳过前n个元素
userList.stream().map(User::getName).skip(2)// 跳过流前两个元素
sorted():排序
userList.stream().map(User::getAge).sorted()//对年龄排序,默认正序,从小到大
userList.stream().map(User::getAge).sorted((a1, a2) -> (a1-a2))//效果同上
userList.stream().map(User::getAge).sorted((a1, a2) -> (a2-a1))//a1,a2调换位置,输出结果为逆序。
也可以使用Comparator 的reverseOrder()来实现逆序:
userList.stream().map(User::getAge).sorted(Comparator.reverseOrder())
上面是先通过map()来获取age流,下面直接通过Comparator.comparing()来排序
List<User> users = userList.stream().sorted(Comparator.comparing(User::getAge)).collect(Collectors.toList());
//逆序处理
List<User> users = userList.stream().sorted(Comparator.comparing(User::getAge).reversed()).collect(Collectors.toList());
当然,list本身有sort()方法,所以还可以:
userList.sort(Comparator.comparing(User::getAge));
userList.sort(Comparator.comparing(User::getAge).reversed());
注意:sorted()和limit(n),skip(n)的前后顺序会影响结果。
Peek():查看流内的数据,peek就是窥视,看一下的意思
下面是经过map(), sort()后,用peek()查看当前流中数据
userList.stream().map(User::getAge).sorted().peek(age -> System.out.println("age:" + age)).collect(Collectors.toList());
anyMatch、allMatch、noneMatch
anyMatch () 存在一个或多个符合条件的,则返回true
allMatch() 全部都符合的条件的,则返回true
noneMatch() 全部都不符合的条件的,则返回true
userList.stream().anyMatch(u -> (u.getAge() > 25)) //true
userList.stream().allMatch(u -> (u.getAge() > 25)) //false
userList.stream().noneMatch(u -> (u.getAge() > 26))// true
findFirst、findAny
findFirst:返回满足条件的第一个元素
findAny:返回任意一个
int name1 = userList.stream().map(User:: getName).findFirst().get();
// findFirst 返回Optional<>类型,调用get()来获取结果元素
int name2 = userList.parallelStream().map(User:: getName).findAny().get();
// 这边用parallelStream(),效果比较明显
Reduce():作用是把 Stream 中元素按照某种规则进行操作
userList.stream().map(User::getAge).reduce((i,j) -> (i+j)).get()//无起始值
userList.stream().map(User::getAge).reduce(0, (i,j) -> (i+j))//有起始值
userList.stream().map(User::getAge).reduce(Integer::sum).get()
count():计数
//计算流中多少个元素
userList.stream().count()
userList.stream().collect(Collectors.counting())
maxBy、minBy:计算最大值和最小值
Integer maxAge = userList.stream().collect(Collectors.maxBy((a1, a2) -> a1.getAge() - a2.getAge())).get().getAge();
Integer maxAge2 =userList.stream().collect(Collectors.maxBy(Comparator.comparing(User::getAge))).get().getAge();
Integer minAge = userList.stream().collect(Collectors.minBy((a1,a2) -> a1.getAge()-a2.getAge())).get().getAge();
Integer minAge2 = userList.stream().collect(Collectors.minBy(Comparator.comparing(User::getAge))).get().getAge();
类似用法还有:
Collectors.averagingInt();//平均值
Collectors.summingInt ()//求和
Collectors.joining(",")//拼接
Collectors.groupingBy();//分组,结果为map<键为分组的属性, value>
Collectors.partitioningBy()//分区,结果为map<true/false, value>,与分组不同,分区的键为false或true
Map<Integer, List<User>> result1 = userList.stream().collect(Collectors.groupingBy(User::getAge));
result1: {25=[User(1, "zhang3", 25), User(3, "wang5", 25)], 26=[User(2, "li4", 26), User(4, "zhao6", 26)]}
Map<Boolean, List<User>> result2 = userList.stream().collect(Collectors.partitioningBy(u-> u.getAge()>25));
result2:{false=[User(1, "zhang3", 25), User(3, "wang5", 25)], true=[User(2, "li4", 26), User(4, "zhao6", 26)]}
参考:
https://developer.ibm.com/zh/articles/j-lo-java8streamapi/
https://www.cnblogs.com/gaopengfirst/p/10813803.html
https://www.cnblogs.com/xxmyz/p/10060002.html
其他关于Stream的优秀文章:
https://mp.weixin.qq.com/s/6NQ1BUFOX8ZqVdXaKR0sPQ