继续接着上一篇我们介绍的,我们继续深入学习 Kotlin Koans。

Task 20:Introduction

接下来的 Task,都是围绕集合展开的,Kotlin 的集合和 Java 的集合差不多,不过 Kotlin 扩展了许多有用的方法。我们先看一下 Show 类的定义:

data class Shop(val name: String, val customers: List<Customer>)

data class Customer(val name: String, val city: City, val orders: List<Order>) {
    override fun toString() = "$name from ${city.name}"
}

data class Order(val products: List<Product>, val isDelivered: Boolean)

data class Product(val name: String, val price: Double) {
    override fun toString() = "'$name' for $price"
}

data class City(val name: String) {
    override fun toString() = name
}

android kotlin的Long相加 kotlin koans_Boo

比较容易,直接用 Shop 中的 customers 转换成 Set 就可以了:

android kotlin的Long相加 kotlin koans_Boo_02

Task 21:Filter; map

android kotlin的Long相加 kotlin koans_移动开发_03

其中我们可以看到 map 方法是:对于给定的规则,返回 List:

/**
 * Returns a list containing the results of applying the given [transform] function
 * to each element in the original collection.
 */
public inline fun <T, R> Iterable<T>.map(transform: (T) -> R): List<R> {
    return mapTo(ArrayList<R>(collectionSizeOrDefault(10)), transform)
}

/**
 * Applies the given [transform] function to each element of the original collection
 * and appends the results to the given [destination].
 */
public inline fun <T, R, C : MutableCollection<in R>> Iterable<T>.mapTo(destination: C, transform: (T) -> R): C {
    for (item in this)
        destination.add(transform(item))
    return destination
}

对于 filter 方法是:返回符合给定规则的 List,规则用于判定该元素是否符合要求,规则返回值为 Boolean

/**
 * Returns a list containing only elements matching the given [predicate].
 */
public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T> {
    return filterTo(ArrayList<T>(), predicate)
}

/**
 * Appends all elements matching the given [predicate] to the given [destination].
 */
public inline fun <T, C : MutableCollection<in T>> Iterable<T>.filterTo(destination: C, predicate: (T) -> Boolean): C {
    for (element in this) if (predicate(element)) destination.add(element)
    return destination
}

android kotlin的Long相加 kotlin koans_移动开发_04

Task 22:All, Any and other predicates

android kotlin的Long相加 kotlin koans_Boo_05

有了上面的经验,这里的问题就好解决了:

android kotlin的Long相加 kotlin koans_List_06

根据名称,我们其实可以很容易猜到是干什么,但是我们这里还是要看看其中具体是怎么实现的:

/**
 * Returns `true` if all elements match the given [predicate].
 */
public inline fun <T> Iterable<T>.all(predicate: (T) -> Boolean): Boolean {
    if (this is Collection && isEmpty()) return true
    // 有一个不符合条件,就返回 false
    for (element in this) if (!predicate(element)) return false
    return true
}

/**
 * Returns `true` if at least one element matches the given [predicate].
 */
public inline fun <T> Iterable<T>.any(predicate: (T) -> Boolean): Boolean {
    if (this is Collection && isEmpty()) return false
    // 有符合条件的,就返回 true
    for (element in this) if (predicate(element)) return true
    return false
}

/**
 * Returns the number of elements matching the given [predicate].
 */
public inline fun <T> Iterable<T>.count(predicate: (T) -> Boolean): Int {
    if (this is Collection && isEmpty()) return 0
    var count = 0
    // 有符合条件的,计数加一,最后返回计数
    for (element in this) if (predicate(element)) count++
    return count
}

/**
 * Returns the first element matching the given [predicate], or `null` if no such element was found.
 */
@kotlin.internal.InlineOnly
public inline fun <T> Iterable<T>.find(predicate: (T) -> Boolean): T? {
    return firstOrNull(predicate)
}
/**
 * Returns the first element matching the given [predicate], or `null` if element was not found.
 */
public inline fun <T> Iterable<T>.firstOrNull(predicate: (T) -> Boolean): T? {
    // 有符合条件的,返回符合条件的这一项
    for (element in this) if (predicate(element)) return element
    return null
}

Task 23:FlatMap

android kotlin的Long相加 kotlin koans_java_07

FlatMap 对集合中的元素去重组成新的集合:

/**
 * Returns a single list of all elements yielded from results of [transform] function being invoked on each element of original collection.
 */
public inline fun <T, R> Iterable<T>.flatMap(transform: (T) -> Iterable<R>): List<R> {
    return flatMapTo(ArrayList<R>(), transform)
}
/**
 * Appends all elements yielded from results of [transform] function being invoked on each element of original collection, to the given [destination].
 */
public inline fun <T, R, C : MutableCollection<in R>> Iterable<T>.flatMapTo(destination: C, transform: (T) -> Iterable<R>): C {
    // 遍历集合中元素,把元素的去重后重组成 List 返回
    for (element in this) {
        val list = transform(element)
        destination.addAll(list)
    }
    return destination
}

android kotlin的Long相加 kotlin koans_数据结构与算法_08

Task 24:Max; min

android kotlin的Long相加 kotlin koans_Boo_09

最大最小的查找是比较常用的操作,结合前面的 FlatMap,可以解决此问题:

android kotlin的Long相加 kotlin koans_移动开发_10

其中我们看看 max 是如何实现的:

/**
 * Returns the first element yielding the largest value of the given function or `null` if there are no elements.
 */
public inline fun <T, R : Comparable<R>> Iterable<T>.maxBy(selector: (T) -> R): T? {
    val iterator = iterator()
    if (!iterator.hasNext()) return null
    var maxElem = iterator.next()
    var maxValue = selector(maxElem)
    // 简单粗暴遍历查找符合条件的最大的一项,是稳定的
    while (iterator.hasNext()) {
        val e = iterator.next()
        val v = selector(e)
        if (maxValue < v) {
            maxElem = e
            maxValue = v
        }
    }
    return maxElem
}

Task 24:Sort

android kotlin的Long相加 kotlin koans_数据结构与算法_11

排序:

android kotlin的Long相加 kotlin koans_Boo_12

看似简单,不过似乎有没那么简单:

/**
 * Returns a list of all elements sorted according to natural sort order of the value returned by specified [selector] function.
 */
public inline fun <T, R : Comparable<R>> Iterable<T>.sortedBy(crossinline selector: (T) -> R?): List<T> {
    return sortedWith(compareBy(selector))
}

/**
 * Returns a list of all elements sorted according to the specified [comparator].
 */
public fun <T> Iterable<T>.sortedWith(comparator: Comparator<in T>): List<T> {
    if (this is Collection) {
       if (size <= 1) return this.toList()
       @Suppress("UNCHECKED_CAST")
       return (toTypedArray<Any?>() as Array<T>).apply { sortWith(comparator) }.asList()
    }
    return toMutableList().apply { sortWith(comparator) }
}

/**
 * Creates a comparator using the function to transform value to a [Comparable] instance for comparison.
 */
@kotlin.internal.InlineOnly
public inline fun <T> compareBy(crossinline selector: (T) -> Comparable<*>?): Comparator<T> =
        Comparator { a, b -> compareValuesBy(a, b, selector) }

/**
 * Compares two values using the specified [selector] function to calculate the result of the comparison.
 * The function is applied to the given values [a] and [b] and return [Comparable] objects.
 * The result of comparison of these [Comparable] instances is returned.
 */
@kotlin.internal.InlineOnly
public inline fun <T> compareValuesBy(a: T, b: T, selector: (T) -> Comparable<*>?): Int {
    return compareValues(selector(a), selector(b))
}

/**
 * Compares two nullable [Comparable] values. Null is considered less than any value.
 */
public fun <T : Comparable<*>> compareValues(a: T?, b: T?): Int {
    if (a === b) return 0
    if (a == null) return -1
    if (b == null) return 1

    @Suppress("UNCHECKED_CAST")
    return (a as Comparable<Any>).compareTo(b)
}

/**
 * Returns a list of all elements sorted according to the specified [comparator].
 */
public fun <T> Iterable<T>.sortedWith(comparator: Comparator<in T>): List<T> {
    if (this is Collection) {
       if (size <= 1) return this.toList()
       @Suppress("UNCHECKED_CAST")
       return (toTypedArray<Any?>() as Array<T>).apply { sortWith(comparator) }.asList()
    }
    return toMutableList().apply { sortWith(comparator) }
}

Task 25:Sum

android kotlin的Long相加 kotlin koans_Boo_13

求和:

android kotlin的Long相加 kotlin koans_Boo_14

/**
 * Returns the sum of all values produced by [selector] function applied to each element in the collection.
 */
public inline fun <T> Iterable<T>.sumByDouble(selector: (T) -> Double): Double {
    var sum: Double = 0.0
    // 遍历并相加
    for (element in this) {
        sum += selector(element)
    }
    return sum
}

Task 26:Group By

android kotlin的Long相加 kotlin koans_java_15

分组:

android kotlin的Long相加 kotlin koans_Boo_16

实现方法:

/**
 * Groups elements of the original collection by the key returned by the given [keySelector] function
 * applied to each element and returns a map where each group key is associated with a list of corresponding elements.
 *
 * The returned map preserves the entry iteration order of the keys produced from the original collection.
 *
 * @sample samples.collections.Collections.Transformations.groupBy
 */
public inline fun <T, K> Iterable<T>.groupBy(keySelector: (T) -> K): Map<K, List<T>> {
    return groupByTo(LinkedHashMap<K, MutableList<T>>(), keySelector)
}

/**
 * Groups elements of the original collection by the key returned by the given [keySelector] function
 * applied to each element and puts to the [destination] map each group key associated with a list of corresponding elements.
 *
 * @return The [destination] map.
 *
 * @sample samples.collections.Collections.Transformations.groupBy
 */
public inline fun <T, K, M : MutableMap<in K, MutableList<T>>> Iterable<T>.groupByTo(destination: M, keySelector: (T) -> K): M {
    for (element in this) {
        val key = keySelector(element)
        // 将新数据添加到 Map 中,如果以前有 Key,添加,否则创建后添加
        val list = destination.getOrPut(key) { ArrayList<T>() }
        list.add(element)
    }
    return destination
}

/**
 * Returns the value for the given key. If the key is not found in the map, calls the [defaultValue] function,
 * puts its result into the map under the given key and returns it.
 *
 * @sample samples.collections.Maps.Usage.getOrPut
 */
public inline fun <K, V> MutableMap<K, V>.getOrPut(key: K, defaultValue: () -> V): V {
    val value = get(key)
    // 在 Map 中根据 Key 判断是否可以找到 Value,如果找不到,在这个 Map 中创建一条
    return if (value == null) {
        val answer = defaultValue()
        put(key, answer)
        answer
    } else {
        value
    }
}

Task 27:Partition

android kotlin的Long相加 kotlin koans_Boo_17

区分(满足条件和不满住条件):

android kotlin的Long相加 kotlin koans_数据结构与算法_18

android kotlin的Long相加 kotlin koans_数据结构与算法_19

/**
 * Splits the original collection into pair of lists,
 * where *first* list contains elements for which [predicate] yielded `true`,
 * while *second* list contains elements for which [predicate] yielded `false`.
 */
public inline fun <T> Iterable<T>.partition(predicate: (T) -> Boolean): Pair<List<T>, List<T>> {
    val first = ArrayList<T>()
    val second = ArrayList<T>()
    // 判断是否满足条件,分别放到第一个第二个参数中
    for (element in this) {
        if (predicate(element)) {
            first.add(element)
        } else {
            second.add(element)
        }
    }
    return Pair(first, second)
}

Task 28:Fold

android kotlin的Long相加 kotlin koans_Boo_20

聚合(Fold),思路是:找出所有商品,将这些商品依次和每一个用户买过的商品做交集,最后剩下的商品就是所有用户都购买了的商品:

android kotlin的Long相加 kotlin koans_Boo_21

/**
 * Accumulates value starting with [initial] value and applying [operation] from left to right to current accumulator value and each element.
 */
public inline fun <T, R> Iterable<T>.fold(initial: R, operation: (acc: R, T) -> R): R {
    var accumulator = initial
    for (element in this) accumulator = operation(accumulator, element)
    return accumulator
}

小结

到这里,我们完成了集合类相关的测试,我们可以看到,集合的基本数据结构,Kotlin 和 Java 区别不大,但是 Kotlin 扩展了集合的方法,这些方法的灵活应用,可以基本满足平时集合相关的操作。要想更灵活的应用,需要我们多加练习,更好的去熟悉。