Kotlin 陣列 (Arrays)
陣列 (Array) 是一種容器,用來存放多個相同型別的資料。
在 Kotlin 中,陣列由 Array 類別表示。它與 Java 的 Array 相容,但功能更強大。
建立陣列
1. 使用 arrayOf()
這是最簡單的建立方式:
val numbers = arrayOf(1, 2, 3) // Array<Int>
val strings = arrayOf("A", "B", "C") // Array<String>
val mixed = arrayOf(1, "A", true) // Array<Any>
2. 基本型別陣列 (Primitive Type Arrays)
為了效能考量 (避免 Boxing Overhead),Kotlin 提供了針對 Primitive Types 的專用陣列類別:
IntArray, ByteArray, ShortArray, CharArray, LongArray ... 等等。
Array<Int> 和 IntArray 是不一樣的!前者是物件陣列 (Integer[]),後者是原生陣列 (int[])。通常建議優先使用 原生陣列,效能較好。val intArr = intArrayOf(1, 2, 3) // 使用常用函式建立
val zeros = IntArray(5) // 建立長度為 5,初始值為 0 的陣列
val initArr = IntArray(5) { i -> i * 2 } // [0, 2, 4, 6, 8] (使用 lambda 初始化)
3. 使用建構子 (Constructor)
如果你想要建立一個非 Primitive 且有特定規則的陣列:
// 建立一個長度 5 的 String Array,內容是 index 的字串版
val arr = Array(5) { i -> "Item $i" }
// ["Item 0", "Item 1", "Item 2", "Item 3", "Item 4"]
4. 空值陣列
如果你需要一個初始化全為 null 的陣列:
val nulls = arrayOfNulls<String>(5) // [null, null, null, null, null]
存取與修改
使用 [] 運算子 (Indexing Operator) 來存取元素。陣列是 Mutable (可變的),所以可以修改內容。
val arr = arrayOf(1, 2, 3)
println(arr[0]) // 1 (Get)
arr[0] = 10 // 修改 (Set)
println(arr[0]) // 10
[] 其實是呼叫了 get() 和 set() 方法的操作符重載。
走訪陣列 (Traversal)
1. 直接走訪元素 (for-each)
for (num in arr) {
println(num)
}
2. 走訪索引 (indices)
for (i in arr.indices) { // 0..arr.size-1
println("Index $i is ${arr[i]}")
}
3. 同時取得索引與值 (withIndex)
for ((index, value) in arr.withIndex()) {
println("Index $index: $value")
}
常見操作與方法
串接 (plus / +)
陣列可以使用 + 號來串接,但注意這會 產生新的陣列:
val a = arrayOf(1, 2)
val b = arrayOf(3, 4)
val c = a + b // [1, 2, 3, 4]
切片 (slice)
取出陣列的一部份:
val arr = arrayOf(0, 1, 2, 3, 4, 5)
val sub = arr.sliceArray(1..3) // [1, 2, 3]
排序與反轉
val unsorted = arrayOf(5, 2, 8, 1)
unsorted.sort() // 原地排序 (In-place sort)
println(unsorted.contentToString()) // [1, 2, 5, 8]
unsorted.reverse() // 原地反轉
統計 (Sum, Max, Min)
適用於數值陣列:
val nums = intArrayOf(10, 20, 30)
println(nums.sum()) // 60
println(nums.average()) // 20.0
println(nums.maxOrNull()) // 30
函式式操作 (Functional API)
雖然 Array 不是 Collection,但它支援幾乎所有 Collection 的操作:
val arr = arrayOf(1, 2, 3, 4, 5)
// 映射 (Transform)
val double = arr.map { it * 2 } // [2, 4, 6, 8, 10] (回傳 list)
// 過濾 (Filter)
val even = arr.filter { it % 2 == 0 } // [2, 4] (回傳 list)
// 檢查
val hasFive = arr.any { it == 5 } // true
val allPositive = arr.all { it > 0 } // true
多維陣列 (Multi-dimensional Arrays)
Kotlin 沒有直接支援 int[][] 這種語法,多維陣列其實就是「陣列的陣列」(Array of Arrays)。
// 建立一個 2x3 的矩陣
// [
// [0, 0, 0],
// [0, 0, 0]
// ]
val matrix = Array(2) { IntArray(3) }
matrix[0][0] = 1
println(matrix[0][0]) // 1
可變參數 (Varargs) 與 Spread Operator (*)
如果函式接收 vararg 參數,你可以傳入多個值,也可以把現有的陣列「攤平」傳進去 (使用 *):
fun printAll(vararg strings: String) {
for (s in strings) println(s)
}
fun main() {
printAll("A", "B", "C")
val arr = arrayOf("D", "E")
// printAll(arr) // 錯誤!型別不符
printAll(*arr) // 正確!使用 * (Spread Operator) 展開陣列
}
陣列內容比較 (Array Equality)
這是最容易犯錯的地方!在 Kotlin (以及 Java) 中,陣列的 == 運算子比較的是 參考 (Reference),也就是「它們是不是同一個物件」,而不是比較內容。
如果要比較兩個陣列的 內容 是否相同,必須使用 .contentEquals():
val a = intArrayOf(1, 2, 3)
val b = intArrayOf(1, 2, 3)
println(a == b) // false (不同的物件)
println(a.contentEquals(b)) // true (內容相同)
如果是多維陣列,則要使用 .contentDeepEquals():
val m1 = arrayOf(intArrayOf(1, 2), intArrayOf(3, 4))
val m2 = arrayOf(intArrayOf(1, 2), intArrayOf(3, 4))
println(m1.contentEquals(m2)) // false (因為裡面存的是 IntArray 物件,參考不同)
println(m1.contentDeepEquals(m2)) // true (深入比較內容)
使用 Array 或 Collection 的時機?
一般建議優先使用 集合 Collections(List, Set, Map) 而不是 Array,原因:
- 支援唯讀 (Read-only):集合可以是唯讀的(例如
List<T>),這提供了更好的控制權。讓你可以編寫意圖清晰、更穩健的程式碼。 - 元素操作的效率:集合介面可以輕鬆地新增或移除元素。陣列的大小是固定的。每次新增或移除元素時,都必須建立一個全新的陣列,這在工作效率上是非常低下的。
- 結構相等性檢查:集合可以直接使用相等運算子
==來檢查兩個集合在結構上是否相等(即它們的內容是否相同)。Arrays 不能對陣列使用==運算子進行結構相等性檢查。你必須改用特殊函式來比較陣列的內容。
適合使用 Array 的情境:
- 極致性能需求:因為陣列在記憶體中是連續儲存的,存取速度通常比 List 稍快(因為 List 實現上可能帶有額外開銷)。另外,Kotlin 提供了專門用於原始型別的陣列,例如 IntArray, BooleanArray, DoubleArray 等,這些陣列在底層避免了裝箱/拆箱 (Boxing/Unboxing) 的性能開銷。
- 建構自訂資料結構:你需要從頭開始建立客製化的資料結構。