數組
先來談談數組,也可參考 Kotlin 快速入門#數組
數組在 Kotlin 中使用 Array
類來表示,它定義了 get
和 set
函數(按照運算符重載約定這會轉變爲 []
)和 size
屬性,以及一些其他有用的成員函數:
class Array<T> private constructor() {
val size: Int
operator fun get(index: Int): T
operator fun set(index: Int, value: T): Unit
operator fun iterator(): Iterator<T>
// ……
}
Library.kt
中 arrayOf()
arrayOfNulls
函數以及Array
構造函數能創建數組:
val args: Array<Int> = arrayOf(1, 2, 3)
val arrayOfNulls = arrayOfNulls<Int>(10) //空數組
val initArray = Array(5, { i -> (i * i).toString() }) //構造函數init
println(arrayOfNulls.size)
NOTE: 與 Java 不同的是,Kotlin 中數組是不型變的(invariant)。這意味着 Kotlin 不讓我們把 Array<String>
賦值給 Array<Any>
,以防止可能的運行時失敗(但是你可以使用 Array<out Any>
, 參見類型投影)。
之前所說的在泛型情況下Kotlin 會把數字和字符自動裝箱成相應包裝類, Arrays.kt
中有以下
ByteArray
CharArray
ShortArray
IntArray
LongArray
FloatArray
DoubleArray
BooleanArray
無裝箱開銷的專門的類來表示原生類型數組, 和 Array
並沒有繼承關係,但是它們有同樣的方法屬性集。它們也都有相應的工廠方法。
val x: IntArray = intArrayOf(1, 2, 3)
x[0] = x[1] + x[2]
數組迭代通過 iterator()
函數返回 Iterator<T>
對象進行迭代:
val iterator = args.iterator()
while (iterator.hasNext()) {
print("" + iterator.next())
}
println()
//forEach
args.iterator().forEach { print(it) }
println()
//for-
for (it in initArray/*.iterator()*/) {
print(it)
}
println()
//下標索引
args.forEachIndexed { index, i -> println("$index = $i") }
NOTE: forEach
forEachIndexed
這些是Array 的擴展函數, 背後實現也是 [for 循環 ](#For 循環)
arrayOf 背後實現
arrayOf(1,2)//vs Java: new Integer[]{1, 2};
intArrayOf(1, 2) //vs Java new int[]{1, 2};
val newArray = intArrayOf(1, 2, *(intArrayOf(3, 4)))
/*vs Java
IntSpreadBuilder var4 = new IntSpreadBuilder(3);
var4.add(1);
var4.add(2);
var4.addSpread(new int[]{3, 4});
int[] newArray = var4.toArray();*/
如上面 的 intArrayOf(vararg elements: Int)
的函數還可以接收數組對象地址,從而新建一個原數據順序的數組。由字節碼分析可以知道,當接收數組對象地址時,arrayOf 複雜了點,主要是PrimitiveSpreadBuilders.kt 這個文件,它下面聲明瞭 PrimitiveSpreadBuilder<T : Any>(private val size: Int)
對象,以及基本類型的對應的子對象 ByteSpreadBuilder、CharSpreadBuilder、DoubleSpreadBuilder、FloatSpreadBuilder、IntSpreadBuilder、LongSpreadBuilder、ShortSpreadBuilder、BooleanSpreadBuilder
看完源碼後,直到關注的只有3個點
- 單個基本類型元素的添加,都是IntSpreadBuilder 等子類
add(value: Int)
實現的,並保存到values
相關類型數組集合裏面 - 單個基本類型數組元素添加,都是PrimitiveSpreadBuilder 基類
addSpread(spreadArgument: T)
實現的,並保存到spreads
數組集合裏面 toArray(values: T, result: T): T
該函數在基類裏完成values、spreads數據拷貝到result 數組中
public abstract class PrimitiveSpreadBuilder<T : Any>(private val size: Int) {
abstract protected fun T.getSize(): Int
//當前 elements pos
protected var position: Int = 0
//用於存放數組的數組集合,且大小同elements size,且position同elements 中一致
@Suppress("UNCHECKED_CAST")
private val spreads: Array<T?> = arrayOfNulls<Any>(size) as Array<T?>
//添加數組到相應位置
public fun addSpread(spreadArgument: T) {
spreads[position++] = spreadArgument
}
//計算新數組的size
protected fun size(): Int {
var totalLength = 0
for (i in 0..size - 1) {
totalLength += spreads[i]?.getSize() ?: 1
}
return totalLength
}
//複製單個元素和單個數組到新數組result中
protected fun toArray(values: T, result: T): T {
//新數組result待插入位置
var dstIndex = 0
//copy values元素到result時,values的開始copy位置
var copyValuesFrom = 0
//for each elements
for (i in 0..size - 1) {
val spreadArgument = spreads[i]
if (spreadArgument != null) {
//不爲null 的spreadArgument肯定是個數組對象
if (copyValuesFrom < i) {
//開始copy這個數組前面的元素(1個或多個)
System.arraycopy(values, copyValuesFrom, result, dstIndex, i - copyValuesFrom)
//copy 後
dstIndex += i - copyValuesFrom
}
val spreadSize = spreadArgument.getSize()
//copy 該數組所有元素到result數組中
System.arraycopy(spreadArgument, 0, result, dstIndex, spreadSize)
dstIndex += spreadSize
//copy 後
copyValuesFrom = i + 1
}
}
//elements 中沒發現數組對象,直接copy values元素到result
if (copyValuesFrom < size) {
System.arraycopy(values, copyValuesFrom, result, dstIndex, size - copyValuesFrom)
}
return result
}
}
//size 是intArrayOf() elements 的size
public class IntSpreadBuilder(size: Int) : PrimitiveSpreadBuilder<IntArray>(size) {
//用於存放單個元素的數組集合,且大小同elements size,且position同elements 中一致
private val values: IntArray = IntArray(size)
//擴展函數,返回數組的長度
override fun IntArray.getSize(): Int = this.size
//添加元素到相應位置
public fun add(value: Int) {
values[position++] = value
}
//調用父類toArray()
public fun toArray(): IntArray = toArray(values, IntArray(size()))
}
kotlin 最終會轉換爲Java字節碼,所以可以自行查看字節碼,分析