16.3 Stream流式编程

16.3 Stream流式编程

标签(空格分隔): kotlin 出版
书名:Kotlin基础教程
作者:陈小默


Stream介绍

java.util.Stream 表示能应用在一组元素上一次执行的操作序列。Stream 操作分为中间操作或者最终操作两种,最终操作返回一特定类型的计算结果,而中间操作返回Stream本身,这样你就可以将多个操作依次串起来。当最终操作发生时则次操作序列结束。Stream 的创建需要指定一个数据源,比如 java.util.Collection的子类,List或者Set。Stream的操作可以串行执行或者并行执行。

为了更好的说明Stream的工作流程,我们需要使用一个Bean类和一个工具方法

class Person(
        var name: String,
        var age: Int,
        var sex: Boolean//true表示女,false表示男
) {
    override fun toString(): String {
        return "Person(name='$name', age=$age, sex=${if (sex) "" else ""})"
    }
}
/**
 * 该方法用来产生个数为num的随机Bean对象列表
 */
fun creator(num: Int): ArrayList<Person> {
    val array = ArrayList<Person>()
    for (i in 0..num) {
        array.add(Person("name$i"
                , (Math.random() * 10 + 20).toInt()//随机年龄在20-30岁之间
                , (Math.random() * 10 + 20).toInt() % 2 == 1)//随机性别
        )
    }
    return array
}

由于在本节编辑时,Kotlin并没有支持此Java8版本中新增的stream()方法,所以这里我们使用扩展为ArrayList增加一个Stream方法

fun ArrayList<Person>.stream(): Stream<Person> {
    return StreamSupport.stream(Spliterators.spliterator(this, 0), false)
}

以下示例中的所有操作在main方法中完成

fun main(args: Array<String>) {
    var people = creator(20)
    ...
}

中间操作方法

Filter 过滤

过滤通过一个predicate接口来过滤并只保留符合条件的元素。非Lambda示例(本章结束时将会附上完成代码):

people.stream().filter(object : Predicate<Person>{
        override fun test(t: Person): Boolean {
            return !t.sex
        }
    })

Stream通过Predicate的test方法的返回值来决定是否保留该元素,所以当返回值为false时,该元素不会进行下一轮操作。该示例的作用是保留所有男性元素。经过Lambda简化后的代码为

people.stream().filter { p -> !p.sex }

Sort 排序
通过一个Comparator接口对一组数据进行排序。

people.stream().sorted(object : Comparator<Person> {
        override fun compare(p1: Person, p2: Person): Int {
            return p1.age - p2.age
        }
    })

这里排序是通过将两个元素按照compare方法规定的方式进行比较来进行排序,这里的作用是将所有元素按照年龄升序排序。Lambda化的实现方式为:

people.stream().sorted { p1, p2 -> p1.age - p2.age }

Map 映射
通过map映射,能够很轻松的按照Function接口依次将元素变换为另一种类型,下面将使用两种方式展现如何将Person对象转换为字符串数据:

    //原始方式
    people.stream().map(object : Function<Person, String> {
        override fun apply(t: Person?): String {
            return t.toString()
        }
    })
    //Lambda方式
    people.stream().map { Person::toString }

最终操作

Match 匹配
匹配操作的目的是从判断Stream中的数据是否满足Predicate接口指定的规则。有如下三种匹配方式:

  • 任意匹配:只要Stream中有一个元素满足要求就算匹配成功
  • 完全匹配:只有Stream中的全部元素满足要求才算匹配成功
  • 完全不匹配:当Stream中的没有一个元素满足要求是匹配成功

这里使用任意匹配举一个例子:

    //原始方式
    val isMatch: Boolean = people.stream().
            anyMatch(object : Predicate<Person> {
                override fun test(p: Person): Boolean {
                    return p.age == 25
                }
            })
    //Lambda方式
    val isMatchWithLambda = people.stream().anyMatch { p -> p.age == 25 }

Count 计数
该操作返回Stream中经过多个中间操作后剩余的元素的个数,其类型是Long。以下通过一个示例查询25岁元素的个数:

    val counter: Long = people.stream().filter { p -> p.age == 25 }.count()
    println(counter)

Reduce 规约
该操作通过一个BinaryOperator接口将Stream中多个元素按照约定方法组合成一个元素后按照Optional接口标准返回。

经过上述介绍,我们已经大体知道这些操作的用法了,现在我们通过一个练习总结刚才的操作:

练习:随机产生20条数据,仅打印年龄最大的5个女性的名字。

people.stream().sorted { p1, p2 -> p1.age - p2.age }
            .filter { p -> p.sex }
            .limit(5)
            .map(Person::name)
            .forEach { str -> println(str) }

并行Streams

Stream的运行模式有串行和并行两种方式。串行方式其实就是在哪个线程创建就在哪个线程执行,而并行方式在运行之初会开启一个线程,然后将自己剩下的操作在该线程中完成。通过在子线程中运行耗时操作可以极大地提升程序的运行效率。在Java中,我们只需要将启动方式从stream()方法改为parallelStream()。在当前Kotlin对于Java8支持不完善的情况下,我们需要自己添加如下方法:

fun ArrayList<Person>.方法改为parallelStream(): Stream<Person> {
    return StreamSupport.stream(Spliterators.spliterator(this, 0), true)
}

可以发现,并行方法仅仅是更改了串行方法的一个标志位。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值