转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/106389071
本文出自【赵彦军的博客】
目录
1、基础概念
Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。
Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
如果你用过 rxjava ,入门就很简单。
2、常见操作符
filter 过滤
var list = mutableListOf("b", "a", "d", "c")
ist.stream()
.filter {
it != "a" //输出元素不是 a 的元素
}
.forEach {
Log.d("yanjun", it)
}
输出结果:
D/yanjun: b
D/yanjun: d
D/yanjun: c
findFirst 查找第一个
var list = mutableListOf("b", "a", "d", "c")
var first = list.stream()
.findFirst() //查找第一个元素
.get()
Log.d("yanjun", first)
输出结果:
D/yanjun: b
sorted 从小到大排序
var list2 = listOf(3, 1, 2)
list2.stream()
.sorted() //默认从小到大排序
.forEach {
Log.d("yanjun", it.toString())
}
输出结果:
D/yanjun: 1
D/yanjun: 2
D/yanjun: 3
map 方法用于映射每个元素到对应的结果
var list = listOf(3, 1, 2)
list.stream()
.map {
it * 2 //所有元素都乘以 2
}
.forEach {
Log.d("yanjun", it.toString())
}
输出结果:
D/yanjun: 6
D/yanjun: 2
D/yanjun: 4
limit 获取指定数量的流
var list = listOf(3, 1, 2)
list.stream()
.limit(2) //只输出前2个数据
.forEach {
Log.d("yanjun", it.toString())
}
输出结果:
D/yanjun: 3
D/yanjun: 1
collect 将流转换成集合和聚合元素
var list = listOf(3, 1, 2)
var list2 = list.stream()
.sorted() //排序 结果为:1 2 3
.collect(Collectors.toList()) //把流数据转换成 list
list2.forEach {
Log.d("yanjun", it.toString())
}
输出结果:
D/yanjun: 1
D/yanjun: 2
D/yanjun: 3
average 求平均值
var list = listOf(3, 1, 2)
var value = list.average() //求平均值
Log.d("yanjun", value.toString())
输出结果:
D/yanjun: 2.0
3、Stream 执行顺序
数据流的链式调用是垂直执行的, 但并不是每个操作符都是垂直的,sorted是水平执行的。
为什么要设计成这样呢?原因是出于性能的考虑。
例子1
var list = mutableListOf("b", "a", "d", "c")
list.stream()
.map {
Log.d("yanjun map", it)
it
}
.forEach {
Log.d("yanjun forEach", it)
}
输出结果:
D/yanjun map: b
D/yanjun forEach: b
D/yanjun map: a
D/yanjun forEach: a
D/yanjun map: d
D/yanjun forEach: d
D/yanjun map: c
D/yanjun forEach: c
例子2
var list = mutableListOf("b", "a", "d", "c")
list.stream()
.map {
Log.d("yanjun map", it)
it
}
.sorted() //排序
.forEach {
Log.d("yanjun forEach", it)
}
输出结果:
D/yanjun map: b
D/yanjun map: a
D/yanjun map: d
D/yanjun map: c
D/yanjun forEach: a
D/yanjun forEach: b
D/yanjun forEach: c
D/yanjun forEach: d
4、数据流复用问题
Java8 Stream 流是不能被复用的,一旦你调用任何终端操作,流就会关闭:
var list = mutableListOf("b", "a", "d", "c")
var stream = list.stream()
//流第一次被使用,使用完毕后,流被关闭,不能重复使用
stream.forEach {
Log.d("yanjun", it)
}
//流第二次被使用,会报异常
stream.forEach {
Log.d("yanjun", it)
}
报异常,程序出错
5、并行流
集合支持parallelStream()方法来创建元素的并行流。或者你可以在已存在的数据流上调用中间方法parallel(),将串行流转换为并行流,这也是可以的。
var list = mutableListOf("b", "a", "d", "c")
list.parallelStream() //创建并行流
.map {
Thread.sleep(5000) //模拟耗时操作
Log.d("yanjun map", Thread.currentThread().name + " " + it)
it
}
.forEach {
Log.d("yanjun forEach", Thread.currentThread().name + " " + it)
}
输出结果:
D/yanjun map: main d
D/yanjun forEach: main d
D/yanjun map: ForkJoinPool.commonPool-worker-1 a
D/yanjun map: ForkJoinPool.commonPool-worker-2 b
D/yanjun forEach: ForkJoinPool.commonPool-worker-1 a
D/yanjun forEach: ForkJoinPool.commonPool-worker-2 b
D/yanjun map: ForkJoinPool.commonPool-worker-3 c
D/yanjun forEach: ForkJoinPool.commonPool-worker-3 c
如您所见,并行流使用了所有的ForkJoinPool中的可用线程来执行流式操作。在持续的运行中,输出结果可能有所不同,因为所使用的特定线程是非特定的。
总结
- 无存储:Stream是基于数据源的对象,它本身不存储数据元素,而是通过管道将数据源的元素传递给操作。
- 函数式编程:对Stream的任何修改都不会修改背后的数据源,比如对Stream执行filter操作并不会删除被过滤的元素,而是会产生一个不包含被过滤元素的新的Stream。
- 延迟执行:Stream的操作由零个或多个中间操作(intermediate operation)和一个结束操作(terminal operation)两部分组成。只有执行了结束操作,Stream定义的中间操作才会依次执行,这就是Stream的延迟特性。
- 可消费性:Stream只能被“消费”一次,一旦消费就会失效。就像容器的迭代器那样,想要再次遍历必须重新生成一个新的Stream。