Kotlin 集合
kotlin 集合-接口架构
kotlin 目前来说,可使用的数据结构类型还是比较弱的,但是对于 java 而言,引入了一些比较友好接口,先来看一张类图:
在 kotlin.collections.Collections.kt 文件中,定义了以下接口:
高清图地址: http://oesb9m88i.bkt.clouddn.com/kt_collection_struct.png
可以清晰的看到 Collection 的架构
- 集合接口类型按照数据结构,分为 List,Set,Map
- 每种集合类型,分为 常规类型(不可变)和 Mutable(可变)类型,其中Mutable 类型继承了 不可变类型,并且增加了修改方法,例如 add,remove 等操作。
根据 kotlin 官方的说明,如果你能够精确控制集合是否可变的,有助于消除 bug 和设计良好的 api。这里怎么理解呢?
Kotlin 集合-抽象实现类
这个后面在解释,我们还是回到这个图,这里只是定义了一些接口和规范,那么实际的实现类呢?
在 kotlin-stdlib/kotlin.collections 包里面,有大量实现了上述接口的抽象类,根据 kotlin 的官方说明,这些抽象类,提供了对应集合类型的骨架支持(skeletal)。我们来总览一眼
这里 kotlin 为我们准备了好多种数据结构的具体实现,包括 List,Map,Set 等。那么,这里的数据接口究竟如何实现的?例如我们看下 AbsList 的实现:
public abstract class AbstractList<out E> protected constructor() : AbstractCollection<E>(), List<E> {
abstract override val size: Int
abstract override fun get(index: Int): E
override fun iterator(): Iterator<E> = IteratorImpl()
// 这里的 indexOffFirst 调用的是 kotli.collections.Collections.kt 里的 拓展函数
override fun indexOf(element: @UnsafeVariance E): Int = indexOfFirst { it == element }
override fun lastIndexOf(element: @UnsafeVariance E): Int = indexOfLast { it == element }
override fun listIterator(): ListIterator<E> = ListIteratorImpl(0)
override fun listIterator(index: Int): ListIterator<E> = ListIteratorImpl(index)
override fun subList(fromIndex: Int, toIndex: Int): List<E> = SubList(this, fromIndex, toIndex)
// SubList 类,辅助 subList() 方法,返回的实际类
private class SubList<out E>(private val list: AbstractList<E>, private val fromIndex: Int, toIndex: Int) : AbstractList<E>(), RandomAccess {
.....
}
// IteratorImpl 类实现,AbsList 迭代器的实际实现类
private open inner class IteratorImpl : Iterator<E> {
/** the index of the item that will be returned on the next call to [next]`()` */
protected var index = 0
override fun hasNext(): Boolean = index < size
override fun next(): E {
if (!hasNext()) throw NoSuchElementException()
return get(index++)
}
}
}
总的来说,AbstractList.kt 主要提供了:
- 迭代器,SubList 的实现
- 提供了 index 系列方法,最终实现的 Collections.kt 里面编写的 List 的拓展函数。
对比 java 的 AbstractList 这里究竟有啥不同的?而且看起来,好像弱很多一样(AbstractList.kt 只有100多行代码,而 AbstractList.java 有700多行代码),那么我们捡一个方法来说明下:
kotlin 中,求出特定元素的下标
//AbstractList.kt
override fun indexOf(element: @UnsafeVariance E): Int = indexOfFirst { it == element }
//Collections.kt
public inline fun <T> List<T>.indexOfFirst(predicate: (T) -> Boolean): Int {
var index = 0
for (item in this) {
if (predicate(item))
return index
index++
}
return -1
}
而在 java 中
public int indexOf(Object o) {
ListIterator<E> it = listIterator();
if (o==null) {
while (it.hasNext())
if (it.next()==null)
return it.previousIndex();
} else {
while (it.hasNext())
if (o.equals(it.next()))
return it.previousIndex();
}
return -1;
}
java 中把对象相等的比较,交给了特定类的 equals() 方法,而 kotlin 中,传入一个函数或者 lambda 表达式{it == item} 作为比较相对的方法,总的来说,kotlin 的写法,看起来简洁明了一些。
其它 Abstract 类的实现也是类似的,大家可以自己参考下源码。
如果我们需要在 kotlin 中使用集合类呢?除了直接使用 java 的集合类的构造方法,创建集合类对象,还有其它办法吗?接下来介绍一个工具类,帮助我们创建集合。
kotlin List工具类 - Collections
然后kotlin 也提供了类似 java 里面 Collections 工具类的一些操作,在 kotlin.collections.collections.kt 文件里面。
这里类主要作用如下:
提供了 EmptyList 单例对象(匿名内部类对象,java 层实现,是 内部类 static 对象),其声明如下:
// Collections.kt 文件中 internal object EmptyList : List<Nothing>, Serializable, RandomAccess { override fun equals(other: Any?): Boolean = other is List<*> && other.isEmpty() override fun hashCode(): Int = 1 override fun toString(): String = "[]" override val size: Int get() = 0 override fun isEmpty(): Boolean = true override fun contains(element: Nothing): Boolean = false override fun containsAll(elements: Collection<Nothing>): Boolean = elements.isEmpty() override fun get(index: Int): Nothing = throw IndexOutOfBoundsException("Empty list doesn't contain element at index $index.") ...... }
可以理解为,这个单例,这是一个List,长度为0,元素为 Nothing,kotlin 之所以提供这个对象,是因为,在传统的 java 语言中,对空的 List,并没有统一,实际上,对于空的 List,无论你是 List 还是 List 都是一样的,kotlin 对这种情况进行了统一,所以这样大的设计是更清晰的。
提供了一些创建集合的工具函数。这里挑选几个函数进行说明:
//根据给定元素,创建一个非空的 List 或者该元素为 null,则返回 EmptyList 对象。 public fun <T : Any> listOfNotNull(element: T?): List<T> = if (element != null) listOf(element) else emptyList() //根据传入的 Size 和创建函数,创建一个 MutableList,第一个参数是List 长度,第二个参数是个函数,表示 //根据 index 创建元素的函数,这里的 repeate 是标准库中遍历函数,表示会从 0-size 重复执行后面的 //lambda 表达式 public inline fun <T> MutableList(size: Int, init: (index: Int) -> T): MutableList<T> { val list = ArrayList<T>(size) repeat(size) { index -> list.add(init(index)) } return list } //二分查找 public fun <T: Comparable<T>> List<T?>.binarySearch(element: T?, fromIndex: Int = 0, toIndex: Int = size): Int { rangeCheck(size, fromIndex, toIndex) var low = fromIndex var high = toIndex - 1 while (low <= high) { val mid = (low + high).ushr(1) // safe from overflows val midVal = get(mid) val cmp = compareValues(midVal, element) if (cmp < 0) low = mid + 1 else if (cmp > 0) high = mid - 1 else return mid // key found } return -(low + 1) // key not found }
总的来说,扩展了一些函数,还算比较实用。
到这里,大家对 Collections 工具类,有个初步的了解了吧,但是这里可以看到,这些函数只是针对 List 的,那么有没有 Map 和 Set 的呢?看下面分析。
Kotlin Map 工具类- Maps
该类位于 kotlin.collections 包,Maps.kt 文件中,主要提供了 EmptyMap 实现以及 Maps 相关工具函数。介绍如下:
提供了 EmptyMap 匿名对象(对应 java 的内部类的静态对象),其实现如下:
private object EmptyMap : Map<Any?, Nothing>, Serializable { private const val serialVersionUID: Long = 8246714829545688274 override fun equals(other: Any?): Boolean = other is Map<*,*> && other.isEmpty() override fun hashCode(): Int = 0 override fun toString(): String = "{}" override val size: Int get() = 0 override fun isEmpty(): Boolean = true override fun containsKey(key: Any?): Boolean = false override fun containsValue(value: Nothing): Boolean = false override fun get(key: Any?): Nothing? = null override val entries: Set<Map.Entry<Any?, Nothing>> get() = EmptySet override val keys: Set<Any?> get() = EmptySet override val values: Collection<Nothing> get() = EmptyList private fun readResolve(): Any = EmptyMap }
类似 EmptyList 定义了 EmptyMap 这一对象。
同样的,这里实现了一些工具函数,常用的如下:
这里做一下归类:
创建一个 Map,包括 mapOf() 系列,创建的是 LinkedHashMap,然后 hashMapOf() 系列创建的是HashMap。
filter() 系列函数,包含了一些取子集的操作,可以针对 value 也可以针对 key 生成子集
操作符重载函数, minus() 和 plus(),这里主要接受,
a: Array
Kotlin Set 工具类 - Sets
该类位于 kotlin.collections 包,Sets.kt 文件中,同样的,这里也是提供了一个 EmptySet 的实现,以及一些工具方法。
EmptySet 的实现:
internal object EmptySet : Set<Nothing>, Serializable { private const val serialVersionUID: Long = 3406603774387020532 override fun equals(other: Any?): Boolean = other is Set<*> && other.isEmpty() override fun hashCode(): Int = 0 override fun toString(): String = "[]" override val size: Int get() = 0 override fun isEmpty(): Boolean = true override fun contains(element: Nothing): Boolean = false override fun containsAll(elements: Collection<Nothing>): Boolean = elements.isEmpty() override fun iterator(): Iterator<Nothing> = EmptyIterator private fun readResolve(): Any = EmptySet }
一些工具函数,具体看下面分析:
//--------创建 Set //创建 HashSet public inline fun <T> hashSetOf(): HashSet<T> = HashSet() //创建 LinkHashSet public inline fun <T> linkedSetOf(): LinkedHashSet<T> = LinkedHashSet() //创建 MutableSet public fun <T> mutableSetOf(vararg elements: T): MutableSet<T> = elements.toCollection(LinkedHashSet(mapCapacity(elements.size))) //创建 SortedSet public fun <T> sortedSetOf(comparator: Comparator<in T>, vararg elements: T): TreeSet<T> = elements.toCollection(TreeSet<T>(comparator))
总的来说,这里提供的函数比少,相对简单。
_Collections List 相关高阶函数
大家可能注意到,第一个集合框架是在 Collections.kt 文件里,然后上面这个工具类也是 Collections.kt 文件,Excuse Me??
事实告诉你,其实 kotlin 里面有三个 Collections.kt 文件。
如下:
前面两个,已经在之前介绍过了,那么这个 _Collections.kt 文件又有啥呢?我们进去看一下。
可以看到,这里都是一些高阶工具函数,简单介绍一下:
associate() 函数
public inline fun <T, K, V, M : MutableMap<in K, in V>> Iterable<T>.associateTo(destination: M, transform: (T) -> Pair<K, V>): M { for (element in this) { destination += transform(element) } return destination }
根据指定的集合,使用变换函数,为集合每个元素(key)生成 Value,然后返回 key-value 可变 Map。
average() 函数
求平均值函数
chunked() 函数
分割函数,根据给定 size,分割 数组。
drop() 函数
删除 前几个元素,并且返回子集合。
fiter() 函数
根据指定的过滤函数,返回子集合
find() 函数
根据指定的过滤函数,查找特定元素,查找成功返回该函数,查找失败,返回 null
groupby() 函数
对当前集合元素,使用传入的分组函数,根据函数结果,生成一个 Map
更多高阶函数
同样的在 _Maps.kt 和 _Sets.kt 文件中,提供了关于 Map 和 Set 的高阶函数,大家使用的时候,可以去查一下,提高开发效率
Kotlin 实现的集合
在接口架构里面,我们看到了很多 AbstractXXX 的接口,但是实际上,却没看到对应的实现类,实际上在现在的版本中, kotlin 使用的数据结构类型,还是引用 java 的数据类型,毕竟是运行在 jvm 上的, kotlin 只是封装了一些用法,然后提供了丰富的语法糖。
在 TypeAliase.kt 文件中,我们可以看到,还是使用了 java 的数据结构,毕竟因为是基于 jvm 的
@SinceKotlin("1.1") public typealias ArrayList<E> = java.util.ArrayList<E>
@SinceKotlin("1.1") public typealias LinkedHashMap<K, V> = java.util.LinkedHashMap<K, V>
@SinceKotlin("1.1") public typealias HashMap<K, V> = java.util.HashMap<K, V>
@SinceKotlin("1.1") public typealias LinkedHashSet<E> = java.util.LinkedHashSet<E>
@SinceKotlin("1.1") public typealias HashSet<E> = java.util.HashSet<E>
internal typealias SortedSet<E> = java.util.SortedSet<E>
internal typealias TreeSet<E> = java.util.TreeSet<E>
实际上,在 kotlin-native 中,提供了使用 kotlin 的实现:
所以,等 kotlin-native 正式使用的时候,kotlin 的集合特性应该会大放异彩的。