继续接着上一篇我们介绍的,我们继续深入学习 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
}
比较容易,直接用 Shop 中的 customers 转换成 Set 就可以了:
Task 21:Filter; map
其中我们可以看到 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
}
Task 22:All, Any and other predicates
有了上面的经验,这里的问题就好解决了:
根据名称,我们其实可以很容易猜到是干什么,但是我们这里还是要看看其中具体是怎么实现的:
/**
* 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
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
}
Task 24:Max; min
最大最小的查找是比较常用的操作,结合前面的 FlatMap,可以解决此问题:
其中我们看看 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
排序:
看似简单,不过似乎有没那么简单:
/**
* 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
求和:
/**
* 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
分组:
实现方法:
/**
* 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
区分(满足条件和不满住条件):
/**
* 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
聚合(Fold),思路是:找出所有商品,将这些商品依次和每一个用户买过的商品做交集,最后剩下的商品就是所有用户都购买了的商品:
/**
* 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 扩展了集合的方法,这些方法的灵活应用,可以基本满足平时集合相关的操作。要想更灵活的应用,需要我们多加练习,更好的去熟悉。