「这是我参与2022首次更文挑战的第12天,活动详情查看:2022首次更文挑战」
前言
在平时开发中,经常使用集和,但是没有做个系统的梳理,这篇文章开始就简单看看这些集和。
本文关于Kotlin的代码版本是1.5.20,Java的代码版本是1.8。
正文
还是先看看我们平时使用的集和,都是继承哪些接口。
顶层接口梳理
Kotlin的接口和Java稍微有一点不同,就是Kotlin中是把可变和不可变给区分开了,就和我们平时定义变量有val和var之分一样,而这些接口是MutableXXX这种命名,下图可以展示Kotlin集和的大致顶层接口情况:
-
Iterator接口:迭代器接口,这个是迭代器接口,什么是迭代器,它的作用就是遍历一个数据集;而在它的接口下,有2个最常见的迭代器接口,一个可变迭代器接口,它表示可以在迭代数据集时,对数据集进行修改;一个是ListIterator,这里可以理解成有序的线性迭代器接口。
-
Iterable接口:可迭代的接口,说明一个类实现了这个接口,它里面必须要实现迭代、遍历数据的能力,而这个能力一般就是通过迭代器实现。
-
Map接口:键值对专用,注意Map接口不是基础Iterable接口的,但是我们平时也可以遍历一个Map,那这为什么不继承Iterable接口呢,我们后面再说。
细分接口梳理
看完上图你可能还不是很理解为什么要这么设计接口,我们接着来看看这些接口都有啥意义。
Iterable
表示该类是可迭代的,而前面说了如何迭代就是通过迭代器,所有该接口非常简单,里面就要求返回一个迭代器。
MutableIterable
前面说过,Kotlin新增的即可,表示可变,即这个类保存的数据集不仅可以遍历查看,还可以增删改,同样是通过返回可变迭代器实现,所以该接口内要求返回一个可变迭代器。
Collection
前面一直说可迭代的类,那这个Collection类就可以看成集和的概念了,也就是我们平时所说的集和,它有个重要特性就是一组同类型数据的数据集,我们认为它就是Collection即集和了。
所以Collection就有了一些集和的雏形函数了,比如是否为空、有多少个元素等。
MutableCollection
同样Kotlin中有可变集和,也就是增加了增删的方法,这时这里有个注意点就是MutableCollection接口有add和remove方法可以增删元素,而其返回的可变迭代器,也可以增删元素,这2个增删使用是有区别的,我们后面细说。
List
终于到了我们熟悉的List了,List接口设计的初衷是有序,定位是处理有序集和,所以在List中的每个元素都有它自己的index即位置坐标,来表示它的顺序,它是第几个,所以List接口就多了关于index的一些方法。
Set
Set的定位是去重和无序,也就是当你需要保证元素不重复时可以使用Set,但是它是无序的,没有下标的概念,所以它的接口定义的方法比较简单。
Map
保存和处理键值对时使用Map,它有个必须要求是键不能重复,按照前面学习,键应该用Set来 保存,而值使用List,这也就说明了Map集和为什么不需要再继承至Collection了,因为它就是Set和List的结合使用,所以它的方法也不复杂。
MutableList
顾名思义,这个就是可变的List,因为List是有序有下班index的概念,所有增删改查都可以以index作为索引。
MutalbeSet
可变的Set,由于Set是无序且重复的,所以其删除就直接根据其元素来进行删除。
MutableMap
可变的Map,由于Map中的键是唯一的,所以其操作都是通过唯一性的键来进行。
说完了集和部分的接口,当然不能少了迭代器的接口,我们继续梳理。
Iterator
前面说了,一个集和能迭代的关键就是有迭代器,所以迭代器的基本功能就是遍历迭代以及获取当前元素。
MutableIterator
可变迭代器,可以在迭代数据的时候,删除元素。
ListIterator
就和前面说的List概念一样,ListIterator迭代器和普通迭代器的区别就是它有了顺序的概念,什么是顺序,也就是下标index,所以它的设计就有了关于index的方法。
MutableListIterator
可变的List迭代器,增加了增删改功能,可以在迭代器中进行元素处理。
其他抽象类
说完了这些接口,会发现里面很多方法很容易实现,所以为了减少具体集和的实现代码行数,这里会有一些基类抽象类,我们也来简单看一下。
AbstractCollection
抽象的骨架Collection列,对一些接口进行了简单实现:
- 这里contains方法直接使用的是一个扩展函数来实现,这个扩展函数是Iterable类的扩展类,直接看一下源码即可:
public inline fun Iterable.any(predicate: (T) -> Boolean): Boolean {
if (this is Collection && isEmpty()) return false
for (element in this) if (predicate(element)) return true
return false
}
这里关于集合的扩展函数非常多,当具体使用时先去查找一下有没有,别先自己实现,同时如果要实现的话,对集合添加扩展函数也是个非常好的例子。
同时这里能用for循环快速遍历,其实是迭代器的运算符重载,如果没有迭代器,这个快速for循环也没法使用。
- toArray方法的copyToArrayImpl的实现是用expect修饰的:
internal expect fun copyToArrayImpl(collection: Collection<*>, array: Array): Array
这是什么含义呢,也就是这个方法在kotlin中定义的,在多平台中期望对应平台实现的方法,也就提现 了kotlin跨平台性,其中在JVM实现中就可以找到这个方法的实现:
internal actual inline fun copyToArrayImpl(collection: Collection<*>): Array =
kotlin.jvm.internal.collectionToArray(collection)
AbstractList
抽象的骨架List,这个类就是对于具体的List做一些简单封装,让具体的List少实现一些方法。
对于抽象的List,这里也实现了能实现的方法,主要点已经在图中标记,重点说明一下:
-
由于List已经具有顺序,所以这里的关于下标index的方法都可以实现,同样是使用扩展函数。
-
这里就已经实现了ListIterator,说了这么久的迭代器,终于要看一下它的实现了,这里定义了内部类,我们来看一下:
//返回迭代器实例,构造函数传入参数0
override fun listIterator(): ListIterator = ListIteratorImpl(0)
private open inner class ListIteratorImpl(index: Int) : IteratorImpl(), ListIterator {
//内部维护一个index,这个index就是外面List的真实index
init {
checkPositionIndex(index, this@AbstractList.size)
this.index = index
}
//如果有前节点,直接判断index是否大于0,下面方法一样
override fun hasPrevious(): Boolean = index > 0
override fun nextIndex(): Int = index
override fun previous(): E {
if (!hasPrevious()) throw NoSuchElementException()
return get(--index)
}
override fun previousIndex(): Int = index - 1
}
会发现这个ListIterator实现的真简单。
3.SubList比较有意思,意思是截取list,但是其实现原理并没有新建一个list,而是使用原list数据,修改其中get方法来实现的。所以使用SubList截取后操作子List也会影响原List。
//这里参数分别是源List,开始和结束index
private class SubList<out E>(private val list: AbstractList<E>,
private val fromIndex: Int, toIndex: Int) :
AbstractList<E>(), RandomAccess {
private var _size: Int = 0
init {
checkRangeIndexes(fromIndex, toIndex, list.size)
this._size = toIndex - fromIndex
}
//get方法获取到的值就是源List公国下标获取
override fun get(index: Int): E {
checkElementIndex(index, _size)
return list[fromIndex + index]
}
override val size: Int get() = _size
}
4.对于equals和hashCode方法,既然这是一个列表,那这种方法肯定都要涉及到每个元素,所以equals判断的是元素顺序和值都一样2个List值才是一样,对于hashCode需要每个元素都参与运算。
override fun equals(other: Any?): Boolean {
if (other === this) return true
if (other !is List<*>) return false
//挨个判断进行equals
return orderedEquals(this, other)
}
internal fun orderedEquals(c: Collection<*>, other: Collection<*>): Boolean {
if (c.size != other.size) return false
val otherIterator = other.iterator()
for (elem in c) {
val elemOther = otherIterator.next()
if (elem != elemOther) {
return false
}
}
return true
}
override fun hashCode(): Int = orderedHashCode(this)
//计算hashCode,每个元素都要参与
internal fun orderedHashCode(c: Collection<*>): Int {
var hashCode = 1
for (e in c) {
hashCode = 31 * hashCode + (e?.hashCode() ?: 0)
}
return hashCode
}
总结
本章只是做一些接口介绍,具体集和我们后面继续介绍。