美文网首页KotlinKotlin专题Kotlin官方文档
Kotlin学习之常用高阶函数:查找

Kotlin学习之常用高阶函数:查找

作者: 程序员丶星霖 | 来源:发表于2018-03-13 22:33 被阅读59次

    Kotlin学习之常用高阶函数:查找

    在一组数据内查找满足特定条件的元素,或在有索引的一组数据中查找满足特定条件元素的索引,都是十分常用的操作,Kotlin为集合、数组、序列、区间等常用的数据结构提供了一些查找元素的高阶函数。

    一、firstOrNull()与lastOrNull()

    firstOrNull:从前向后查找特定的元素,如果找到满足条件时的元素就返回这个元素,如果没有找到就返回null。
    lastOrNull:从后向前查找特定的元素,如果找到满足条件时的元素就返回这个元素,如果没有找到就返回null。

    Kotlin同时也提供了函数名比较短的find()findLast()函数来简写这两个函数,与firstOrNull()与lastOrNull()作用完全相同。

    Array<out T>.lastOrNull()函数的实现:

    inline fun <T> Array<out T>.lastOrNull(predicate: (T) -> Boolean): T? {
      for (index in this.indices.reversed()) {
        val element = this[index]
        if (predicate(element)) 
        return element
      }
      return null
    }
    

    Kotlin这里用Array.indices.reversed()函数来实现从后向前的查找,reversed()函数最终通过调用java.util.Collections.reverse()函数实现反转。

    与此类似,firstOrNull()函数从前向后遍历数组,对每个元素调用传入的Boolean函数predicate,如果predicate返回true则返回这个元素,如果没有元素符合条件则返回null。

    因为这两个函数都可能返回null,在使用时可以配合Elvis操作符来处理查找不到合适元素的情况:

    val array = intArrayOf(0, 1, 2, 3)
    val findOdd = array.firstOrNull { it % 2 == 1 } ?: -1
    println(findOdd)
    

    通过firstOrNull()函数查找array钟第一个奇数,如果没有找到则返回-1。

    firstOrNull()函数返回Int?,这是一个可空类型,但我们在它后面用了Elvis操作符,处理了它为null时的情况,这时候Kotlin编译器就会很聪明地把findOdd类型推断为不可空的Int类型,我们在这之后调用findOdd的方法就不用再担心空安全的问题了。

    二、first()与last()

    first():用来取第一个元素;
    last():用来取最后一个元素。

    这两个函数功能与firstOrNull()和lastOrNull()相同,唯一不同的是,这两个函数会在查找不到元素时抛出一个NoSuchElementException异常

    // Array<out T>.first()
    throw NoSuchElementException("Array contains no element matching the predicate.")
    

    因为不会返回null,可以在保证能找到元素的情况下使用这两个函数,避免空检查;但在不保证能找到元素时不建议使用,毕竟处理null比处理异常要简单的多。

    三、indexOfFirst()与indexOfLast()

    这两个函数返回的是满足特定条件的元素的索引,如果没有找到满足条件的元素则返回-1。

    Kotlin为数组、集合、区间等数据结构扩展了indexOf()lastIndexOf()函数,它们都能查找特定元素在一组数据中的索引,那indexOfFirst()和indexOfLast()函数可以视为这两个函数的加强版,它们能根据复杂的条件查找特定的元素索引。

    四、maxBy()与minBy()

    Kotlin为数组、集合、区间等数据结构扩展了max()和min()函数,能够找到最大和最小的元素,但它们只适用于实现了Comparable接口的类型,也只支持默认的排序方式。

    如果数据类型没有实现Comparable类型,或不希望使用默认的排序方式,就可以用maxBy/minBy+Lambda表达式的方法。

    Array<out T>.maxBy()的实现:

    inline fun <T, R : Comparable<R>> Array<out T>.maxBy(selector: (T) -> R): T? {
        if (isEmpty()) return null
        var maxElem = this[0]
        var maxValue = selector(maxElem)
        for (i in 1..lastIndex) {
            val e = this[i]
            val v = selector(e)
            if (maxValue < v) {
                maxElem = e
                maxValue = v
            }
        }
        return maxElem
    }
    

    可以注意到以下两点:

    • 当数组为空时返回null,因此需要空处理;
    • 传入的selector函数,返回值类型必须实现Comparable接口,maxBy()函数可以视为map(selector).max(),即先将元素映射为Comparable集合,再调用max()函数找到最大值。

    在实际应用中,可以取一个复杂对象的某一个属性来作为排序的依据:

    class Person(val name: String, var age: Int)
    fun main(vararg args: String) {
        val people = arrayOf(
                Person("Alex", 19),
                Person("Bob", 34),
                Person("Candy", 23)
        )
        val oldest = people.maxBy { it.age }!!
        println("Oldest one is ${oldest.name}")
    }
    

    上面的例子中使用了非空操作符!!,它的真正作用就是告诉编译器,这里绝对不会是null。

    五、single()和singleOrNull()

    同样是查找满足特定条件的元素,这两个函数还要求满足条件的元素有且只有一个。如果不存在满足条件的元素或者满足条件的元素超过一个,single()函数会抛出异常,singleOrNull()函数则会返回null。

    Array<out T>.single()函数的实现:

    inline fun <T> Array<out T>.single(predicate: (T) -> Boolean): T {
        var single: T? = null
        var found = false
        for (element in this) {
            if (predicate(element)) {
                if (found) throw IllegalArgumentException("Array contains more than one matching element.")
                single = element
                found = true
            }
        }
        if (!found) throw NoSuchElementException("Array contains no element matching the predicate.")
        return single as T
    }
    

    可以看到,如果不止一个元素满足predicate函数,single()函数会抛出IllegalArgumentException,如果不存在元素满足predicate函数,则抛出NoSuchElementException。

    在查找具有唯一性的数据时,可以用这两个函数。
    single():适用于确保能查找到的情况,可以避免空检查;
    singleOrNull():适用于不确保能查找到的情况,需要处理null。

    学海无涯苦作舟

    朋友们可以关注我的微信公众号,一起进步!!!

    Android成长录.jpg

    相关文章

      网友评论

        本文标题:Kotlin学习之常用高阶函数:查找

        本文链接:https://www.haomeiwen.com/subject/qvztqftx.html