Kotlin 提高篇——數組

數組

先來談談數組,也可參考 Kotlin 快速入門#數組

數組在 Kotlin 中使用 Array 類來表示,它定義了 getset 函數(按照運算符重載約定這會轉變爲 [])和 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.ktarrayOf() 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字節碼,所以可以自行查看字節碼,分析

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章