Java8中Stream的使用
首先在介绍Stream之前,看一个案例:找出所有Sex为“nan”的人,然后以年龄降序排序,分别统计其收入。
需要的Bean对象
class Person(
val name: String,
val sex: String,
val age: Int,
val inCome: Int
) {
override fun toString(): String {
return "Person(name='$name', sex='$sex', age=$age, inCome=$inCome)"
}
}
在Java7中我们通常实现方式如下:
@Test
fun demoJava7() {
val persons = arrayListOf<Person>()
persons.add(Person("zsc", "nan", 13, 270000))
persons.add(Person("fj", "nan", 23, 250000))
persons.add(Person("csx", "nan", 19, 280000))
persons.add(Person("xxx", "nv", 18, 240000))
val curPersons = arrayListOf<Person>()
for (i in 0 until persons.size) {
if (TextUtils.equals(persons[i].sex, "nan")) {
curPersons.add(persons[i])
}
}
println("测试1:" + curPersons.toString())
curPersons.sortWith(Comparator { o1, o2 -> o2.age.compareTo(o1.age) })
println("测试2:" + curPersons.toString())
val personList = arrayListOf<Int>()
for (i in 0 until curPersons.size) {
personList.add(curPersons[i].inCome)
}
println("测试3:" + personList.toString())
}
发现上面代码比较冗杂,先要进行for循环进行筛选,再进行Comparator比较进行排序,将排序的结果进行for循环,取出inCome值,放入一个新的集合中。
在Java8 中使用Stream之后,可以采用如下方式:
@Test
fun demoJava8() {
val persons = arrayListOf<Person>()
persons.add(Person("zsc", "nan", 13, 270000))
persons.add(Person("fj", "nan", 23, 250000))
persons.add(Person("csx", "nan", 19, 280000))
persons.add(Person("xxx", "nv", 18, 240000))
val curPersons = persons.parallelStream()
.filter { person -> TextUtils.equals(person.sex, "nan") }
.sorted { o1, o2 -> o2.age.compareTo(o1.age) }
.map { person -> person.inCome }
.collect(Collectors.toList())
println("测试:" + curPersons.toString())
}
通过上面的案例,发现Stream采用的响应式编程对集合进行操作,有点类似Iterator。
1、Stream是什么?
Stream 是Java8引入的一个工具类,简称流。为了满足我们的需求,通过计算,将流操作组合都流管道中。流管道包括一个源(可能是一个数组,一个集合,一个生成器函数,一个I/O通道等),零个或多个中间操作(它们将一个流转换成另一个流)和一个终端操作(产生结果或副作用,例如Stream#count()或Stream#forEach(Consumer)。流是懒惰的,仅在启动终端操作时才对数据源进行计算,并且仅在需要时才使用源元素。
中间操作返回一个新的流。他们总是懒惰 ; 执行诸如这样的中间操作 filter()实际上并不执行任何过滤,而是创建一个新的流,该流在遍历时将包含与给定谓词匹配的初始流的元素。在执行管道的终端操作之前,不会开始遍历管道源。
对于基本数据类型,目前仅支持:IntStream、LongStream、DoubleStream
2、Stream常用构建
这里只有介绍我们经常碰见到几种构建方式:a.直接构建,b.数组构建,c.集合构建
@Test
fun createStream() {
//1.通过Stream.of() 方式构建
val stream = Stream.of("a", "b", "c")
//2.数组Arrays
val strArrays = arrayOf("a", "b", "c")
val stream1 = Stream.of(strArrays)
val stream2 = Arrays.stream(strArrays)
//3.通过集合创建
val list = Arrays.asList(strArrays)
val stream3 = list.stream()
//并行流
val parallelStream = list.parallelStream()
//对于基本数据类型,目前还支持IntStream、LongStream、DoubleStream
//换一种方式 Stream<Integer>、Stream<Long>、Stream<Double>
var arrayOf = arrayOf(1, 2, 3, 4)
// val stream5 = IntStream.of(arrayOf).forEach(System.out::println)
val stream4 = IntStream.of(1, 2, 3, 4)
}
思考:上面stream5构造方式对不对?想知道答案,大家动动手呗
基本数据类型构造开闭区间:
@Test
fun baseDataStream() {
println("--------------")
IntStream.of(1, 2, 3, 4).forEach(System.out::println)
//打印结果是1、2、3、4
println("------左闭右开--------")
IntStream.range(1, 3).forEach(System.out::println)
//打印结果是1、2、3
println("-------左闭右闭-------")
IntStream.rangeClosed(1, 6).forEach(System.out::println)
//打印结果是1、2、3、4、5、6
println("--------------")
DoubleStream.of(1.2, 1.3, 1.4).forEach(System.out::println)
//打印结果是1.2、 1.3、 1.4
println("--------------")
LongStream.of(1, 4).forEach(System.out::println)
//打印结果是1、4
println("------左闭右开--------")
LongStream.range(1, 4).forEach(System.out::println)
//打印结果是1、2、3
println("------左闭右闭--------")
LongStream.rangeClosed(1, 4).forEach(System.out::println)
//打印结果是1、2、3、4
println("--------------")
}
在基本数据类型中,range(n,p)代表的是左闭右开,及包含n但不包含p;rangeClosed(n,p)代表的是左闭右闭,及包含n也包含p。
3、Stream的方法介绍
-
allMatch :返回值boolean
返回此流的所有元素是否满足条件,如果都满足的话,返回为true,否则返回false。
注意:如果流为空,则直接返回true,不做匹配操作。 -
anyMatch : 返回值boolean
只要此流的任意一个元素满足条件,就返回true,否则返回false。
注意:如果流为空,则直接返回false,不做匹配操作。 -
noneMatch :返回值boolean
此流所有元素都不满足条件,就返回true,否则返回false。
注意:如果流为空,则直接返回false,不做匹配操作。@Test fun testMatch() { val allMatch = Stream.of(3, 4, 5) .allMatch { n -> n > 4 } println("测试1:" + allMatch) val list = arrayListOf<String>() val allMat = list.stream().allMatch { TextUtils.equals(it, "c") } println("测试2:" + allMat) val anyMatch = Stream.of(3, 4, 5) .anyMatch { n -> n > 4 } println("测试3:" + anyMatch) val anyMatch1 = list.stream() .anyMatch { TextUtils.equals(it, "c") } println("测试4:" + anyMatch1) val noneMatch = Stream.of(3, 4, 5) .noneMatch { n -> n > 4 } println("测试5:" + noneMatch) val noneMatch1 = list.stream() .noneMatch { TextUtils.equals(it, "c") } println("测试6:" + noneMatch1) }
-
map :返回值类型是Stream
对包含的元素按照转换函数进行转换操作,返回一个新的Stream。为了提高处理效率,官方已封装好了,三种变形:mapToDouble,mapToInt,mapToLong。其实很好理解,如果想将原Stream中的数据类型,转换为double,int或者是long是可以调用相对应的方法。
@Test fun testMap() { val list = arrayListOf<String>("a", "B", "c") val collect = list.stream() .map(String::toUpperCase) .collect(Collectors.toList()) println("----:" + collect.toString()) }
如上图案例,将 a、B、c 都变成大写,map相当如将a、B、c 遍历之后,将其元素大写。
思考:将 a、B、c三个元素都转换成大写D
fun testMap() { val list = arrayListOf<String>("a", "B", "c") val collect = list.stream() .map { str -> "D" } .collect(Collectors.toList()) println("----:" + collect.toString()) }
-
flatMap 返回值类型Stream
流中的每个元素替换为通过将提供的映射函数应用于每个元素而生成的映射流的内容而得到的结果。
@Test fun testFlatMap() { val list1 = arrayListOf<Person>() list1.add(Person("zsc", "nan", 13, 270000)) list1.add(Person("fj", "nan", 23, 250000)) list1.add(Person("csx", "nan", 19, 280000)) list1.add(Person("xxx", "nv", 18, 240000)) val list2 = arrayListOf<Person>() list2.add(Person("ll", "nan", 15, 270000)) list2.add(Person("jj", "nan", 33, 250000)) list2.add(Person("ss", "nan", 14, 280000)) list2.add(Person("sh", "nv", 58, 240000)) val list3 = arrayListOf<Person>() list3.add(Person("mm", "nan", 15, 27000)) list3.add(Person("ii", "nan", 33, 25000)) list3.add(Person("poo", "nan", 14, 2000)) list3.add(Person("shs", "nv", 58, 2400)) val list4 = arrayListOf<ArrayList<Person>>() list4.addAll(listOf(list1)) list4.addAll(listOf(list2)) list4.addAll(listOf(list3)) //把Stream中的层级结构扁平化并返回Stream val stream = list4.stream() .flatMap { childList -> childList.stream() } .collect(Collectors.toList()) //展开多个List合并到一个新list stream.stream() .forEach(System.out::println) }
首先将list4中三个list,通过flatMap转化为一个list。打印结果如下:
-
filter返回数据类型Stream
过滤满足添加的元素。
@Test fun testFilter() { val list = Stream.of(1, 2, 3, 4, 5, 6) .filter { n -> n % 2 == 0 } .collect(Collectors.toList()) println("测试:" + list) }
-
peek 没有返回数据类型
功能与map相似,但是对每个元素执行操作,不影响原来数据操作。
@Test fun testPeek() { val arrayListOf = arrayListOf<Int>() val list = Stream.of(1, 2, 3, 4, 5) .filter { n -> n > 3 } .peek { arrayListOf.add(it + 2) println("it:" + it) } .map { k -> k * k } .peek { e -> System.out.println(e) } .collect(Collectors.toList()) println("测试:" + list) println("测试:" + arrayListOf) }
打印结果:
peek不能对原来的元素进行更改,只能操作赋值给别的元素,不能更改它本身,就如不能操作peek里面的it。 -
reduce 返回值Stream
给一个初始值和一个运算法则,将每一个运算按照运算法则进行计算。
@Test fun testReduce() { val reduce = Stream.of(2, 2, 3, 4) .reduce(1, BinaryOperator { t, u -> println("t:" + t + " u:" + u) t * u }) val reduce1 = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum) //如果没有给初始化,返回的是Optional val reduce2 = Stream.of(1, 2, 3, 4).reduce(Integer::sum) println("测试:" + reduce) println("测试:" + reduce1) println("测试:" + reduce2.get()) }
reduce获得过程如下:首先t=1,u=2,将计算得到的值赋值给t=2;第二次t=2,u=2,再次将计算的值赋值给t=4;第三次t=4,u=3,再次将计算的值赋值给t=12;第四次t=12,u=4计算得到48。 -
findFirst返回值为Stream
找到一个满足条件的值。
注意:查找的第一个值不能是null,否则报空NullPointException@Test fun testFindFirst() { val list = arrayListOf<Any?>() list.add("1") list.add(null) list.add("1") // val optional2 = Stream.of("", "one", "two", "three").findFirst() val optional = list.stream().findFirst() val value = Optional.ofNullable(optional) .map { str -> str.get() } .orElse("str is null") println("测试:" + value) }
思考:optional2会不会报错,请大家自己尝试
-
limit返回Stream
返回原有strem中前n个元素的Strem。
@Test fun testLimit() { val list = Stream.of(2, 1, 3, 6, 7, 8, 4, 5, 9) .limit(3) .peek(System.out::println) .collect(Collectors.toList()) println("测试:" + list) }
返回结果:[2, 1, 3]
-
skip 返回Stream
返回原有strem中后n个元素的Strem
@Test fun testSkip() { val list = Stream.of(2, 1, 3, 6, 7, 8, 4, 5, 9) .skip(3) .peek(System.out::println) .collect(Collectors.toList()) println("测试:" + list) }
测试结果:[6, 7, 8, 4, 5, 9]
-
sorted 返回Stream
按照某一个规则进行排序,返回新的Stream。
@Test fun testSorted() { val persons = arrayListOf<Person>() persons.add(Person("zsc", "nan", 13, 17000)) persons.add(Person("fj", "nan", 23, 15000)) persons.add(Person("xsd", "nv", 18, 160000)) persons.add(Person("csx", "nan", 19, 18000)) persons.add(Person("xxx", "nv", 18, 14000)) val curPersons = persons.parallelStream() .sorted { o1, o2 -> o2.age.compareTo(o1.age) } .collect(Collectors.toList()) println("测试:" + curPersons.toString()) }
上面的案例是对persons进行按照年龄倒序排序。
此篇只是对于Stream进行常用基本方法介绍。希望对大家有所帮助。
Demo链接地址:https://github.com/muxiaosi/StreamDemo