前段时间公司书架多了一本《Java8 实战》,毕竟久闻lambda的大名,于是借来一阅。这一看,简直是惊为天人啊,lambda,stream,java8里简直是满脑子骚操作,看我的一愣一愣的。我甚至是第一次感觉到了什么叫优雅。
本文主要介绍java8中的流处理,看看java8是怎么愉快的玩耍集合的,让我们来一起感受java8的魅力吧!
我就随便举个例子,看看Stream有多优雅。
1 | // 对苹果按颜色汇总并计算数量 |
一、lambda表达式
虽然本文重点是stream,但是stream中需要传递lambda表达式,所以简单介绍一下lambda表达式。lambda表达式其实就是匿名函数(anonymous function),是指一类无需定义标识符的函数或子程序。
java中匿名函数的表现形式,只留下入参和方法体中的内容
1 | // 普通函数 |
诶,过去我们都用对象调方法的,你弄这个没名的东西啥时候用啊?
java中我们通过函数式接口来使用这种匿名函数。
1.java中只包含一个未实现方法的接口。其中可以有与Object中同名的方法和默认方法(java8中接口方法可以有默认实现)。
2.java中函数式接口使用@FunctionalInterface进行注解。Runnable、Comparator都是函数式接口。
3.java.util.function包下为我们提供很多常用的函数式接口,例如Function<T,R>等。
用法举例:
1 | // 实现Runnable中的run方法,替代匿名内部类。 |
使用lambda表达式可以简化大量的模板代码,并且可以向方法直接传递代码。
总之
1 | 方法出参入参来自函数式接口 |
好了,不多啰嗦了,如果感兴趣推荐下面的文章或《Java8实战》的前三章。
二、Stream
流是什么?
Java API的新成员,它允许你使用声明式方式处理数据集合(类似sql,通过查询语句表达,而不是临时编写一个实现)。
如果有人说lambda表达式不易于理解,那还勉强可以接受(其实过于复杂的lambda缺失不好阅读,但通常lambda不会做太复杂的实现),但流真的非常的易懂易用。这个语法糖真的是甜死了。
1.流只能使用一次,遍历结束就代表这个流被消耗掉了
2.流对集合的操作属于内部迭代,是流帮助我们操作,而不是外部迭代
3.流操作包含:数据源,中间操作链,终端操作三个部分。
基础流操作
1 | List<Double> collect = list.stream() |
Apple::getPrince<=>i -> i.getPrince()
可以看做是仅涉及单一方法的语法糖,效果与lambda表达式相同,但可读性更好。
同理
下面列表为常见操作
中间
操作 | 类型 | 作用 | 函数描述 | 函数 |
---|---|---|---|---|
filter | 中间 | 过滤 | T -> boolean | Predicate |
sorted | 中间 | 排序 | (T,T)->int | Comparator |
map | 中间 | 映射 | T->R | Function<T,R> |
limit | 中间 | 截断 | ||
distinct | 中间 | 去重,根据equals方法 | ||
skip | 中间 | 跳过前n个元素 |
终端
操作 | 类型 | 作用 |
---|---|---|
forEach | 终端 | 消费流中的每个元素,使用lambda进行操作 |
count | 终端 | 返回元素个数,long |
collect | 终端 | 将流归约成一个集合,如List,Map甚至是Integer |
筛选与切片
1 | List<String> strings = Arrays.asList("Hello", "World"); |
归约操作reduce
1 | List<Integer> integers = Arrays.asList(12, 3, 45, 3, 2,-1); |
reduce做的事情是取两个数进行操作,结果返回取下一个数操作,以次类推。
Optional是java8引入的新类,避免造成空指针异常,在集合为空时,结果会包在Optional中,可以用isPresent()方法来判断是否为空值。
无初始值的情况下可能为空,故返回Optional
中间
操作 | 类型 | 作用 | 函数描述 | 函数 |
---|---|---|---|---|
flatmap | 中间 | 使通过的流返回内容 | T -> boolean | Predicate |
终端
操作 | 类型 | 作用 |
---|---|---|
anyMatch | 终端 | 返回boolean,判断是否有符合条件内容 |
noneMatch | 终端 | 返回boolean,判断是否无符合条件内容 |
allMatch | 终端 | 返回boolean,判断是全为符合条件内容 |
findAny | 终端 | Optional |
findFirst | 终端 | Optional |
reduce | 终端 | Optional |
数值流
包装类型的各种操作都会有拆箱操作和装箱操作,严重影响性能。所以Java8为我们提供了原始数值流。
1 | // 数值流求平均值 |
下面列表为常见数值流操作操作
中间
操作 | 类型 | 作用 |
---|---|---|
rangeClosed(1,100) | 中间 | 生成随机数(1,100] |
range(1,100) | 中间 | 生成随机数(1,100) |
boxed() | 中间 | 包装成一般流 |
mapToObj | 中间 | 返回为对象流 |
mapToInt | 中间 | 映射为数值流 |
终端,终端操作与List
构建流
值创建
1
Stream<String> s = Stream.of("java","python");
数组创建
1
2int[] i = {2,3,4,5};
Stream<int> = Arrays.stream(i);由文件生成,NIO API已经更新,以便利用Stream API
1
Stream<String> s = Files.lines(Paths.get("data.txt"),Charset.defaultCharset());
由函数创建流:无限流
1
2
3
4
5
6
7
8// 迭代
Stream.iterate(0,n->n+2)
.limit(10)
.forEach(System.out::println);
// 生成,需要传递实现Supplier<T>类型的Lambda提供的新值
Stream.generate(Math.random)
.limit(5)
.forEach(System.out::println);
三、总结
至此,本文讲述了常见的流操作,目前排序、筛选、求和、归约等大多数操作我们都能实现了。与过去相比,操作集合变的简单多了,代码也变的更加简练明了。
目前Vert.x,Spring新出的WebFlux都通过lambda表达式来简化代码,不久的将来,非阻塞式框架的大行其道时,lambda表达式必将变的更加重要!
至于开篇见到的分组!!!下篇文章见~
参考资料: