【Kotlin】第4課-陣列(Array)

陣列是能夠存放多個相同型態資料的一種物件。透過陣列的索引(index)並搭配迴圈敘述,可以輕鬆地存取裡面的元素(element),而不必替所有資料宣告不同的變數。

本文會講解如何宣告一維與二維陣列,並介紹各種讓迴圈存取的方式。最後練習使用具備可變長度參數的方法。

 一、宣告基本型態陣列

Kotlin 已經有內建一些陣列物件,如 ByteArray、IntArray、DoubleArray 等。它們正好是存放基本資料型態的元素,宣告時也有對應的建構式可使用。以下是宣告整數陣列的範例,這個寫法僅僅只是宣告,尚未賦予元素值。

val scores = IntArray(5)

若在建立陣列時,元素值是已知的,可以用下面的方式同時完成宣告。

val scores = intArrayOf(68, 90, 76, 88, 92)

不論是這一課的陣列或是後面學到的集合,如果是像這樣用「Of」結尾的方法宣告,就代表可以同時給予元素值。


二、宣告其他型態陣列

然而字串(String)並非基本資料型態,而且未來也有機會宣告其他物件型態的陣列,這時就要用另外的方法。以下是宣告字串陣列的範例,其他資料型態也是相同的做法。

val names = arrayOfNulls<String>(5)

使用 arrayOfNulls 方法,並定義泛型類別與容量,可以建立元素為指定資料型態的陣列。要注意的是,透過該方法建立的陣列,元素預設值固定為 null。因此不論泛型類別提供的是 String 或 String?,元素型態一律為 String?。

若在建立陣列時,元素值是已知的,可以用下面的方式同時完成宣告。

val names = arrayOf("Anna", "Bonnie", "Cynthia", "Darcy", "Edison")

使用 arrayOf 方法,不需定義泛型類別,Kotlin 會根據元素的資料型態自動決定。特別的是,除非提供的元素中有包含 null 值,元素型態才會是 String?,否則正常情況下,皆為 String 型態。

三、存取陣列元素

建立好陣列後,便能在裡面存放元素,或是在需要的時候取出。存放與取出的方式與 Java 相同。

val scores = IntArray(5)

// 存放元素
scores[0] = 68
scores[1] = 90
scores[2] = 76
scores[3] = 88
scores[4] = 92

// 取出元素,印出68
println(scores[0])

延續前面的陣列,第一種用迴圈走訪元素的方式是「for each」。透過「in」語法能將元素逐一取出,賦值給臨時變數。

for (score in scores) {
println(score)
}

第二種用迴圈走訪的方式是透過陣列索引。陣列的 indices 方法會回傳索引的範圍,其順序為0到陣列容量 - 1。或者使用 size 方法自行建立範圍。這兩種都可以用在 for 迴圈中。

// 由陣列提供索引範圍
for (i in scores.indices) {
println(scores[i])
}

// 自行建立範圍
for (i in 0 until scores.size) {
printn(scores[i])
}

第三種用迴圈走訪的方式是同時對索引和元素做「for each」。陣列的 withIndex 方法會回傳多個 IndexedValue 物件,它包含索引與元素兩項資料,與 key-value 的概念類似。我們用兩個變數分別接收它們,並在迴圈中使用。

val names = arrayOf("Anna", "Bonnie", "Cynthia", "Darcy", "Edison")

for ((i, value) in names.withIndex()) {
println("${i + 1}. ${value.toUpperCase()}")
}

讀者可嘗試添加一個 null 元素,此時會出現錯誤訊息。這是因為元素型態從 String 轉為 String? 了,使用 toUpperCase 方法時需加上「?」或「!!」符號。


四、二維陣列

如果需要多維陣列來儲存更多資料,只要將第二節介紹的宣告方式加以運用,即可建立出二維以上的陣列。也就是說,可以看做一維陣列中的元素又是陣列。

val names = arrayOf(
arrayOf("Anna", "Bonnie", "Cynthia"),
arrayOf("Darcy", "Edison")
)

// 印出Darcy
println(names[1][0])

接下來結合第3課的迴圈標籤,來實作一個走訪二維陣列的方法。

fun findName(names: Array<Array<String>>, target: String): Boolean {
var isFound = false

outer@ for (i in names.indices) {
for (j in names[i].indices) {
if (names[i][j] = target) {
isFound = true
println("${target}的位置在:$i, $j")
break@outer
}
}
}

return isFound
}

此處使用了雙層迴圈來讀取陣列元素。外層迴圈給予名為 outer 的標籤,並在找到目標字串後,透過這個標籤來跳離。


五、方法的可變長度參數

前面在宣告陣列並同時定義元素時,會使用「Of」系列的方法。仔細觀察它們接受的參數,會發現除了型態,還多出「vararg」的字眼,意思為 variable arguments。

「vararg」字眼代表使用該方法時,能夠傳入不限數量的同型態參數。接下來讓我們結合前面所學,來實作像這樣子的方法。

fun adjustScore(vararg scores: Int): IntArray {
// 宣告相同容量的陣列
val result = IntArray(scores.size)

for (i in scores.indices) {
// 對每個分數進行調整,並存入新陣列
val score = Math.sqrt(scores[i].toDouble()) * 10
result[i] = score.toInt()
}

return result
}

在方法參數前加上「vararg」,就能傳遞多個參數,並且它們會統整成一個陣列。這段範例程式是用將分數透過「開根號再乘10」的方式調整,並回傳新的分數陣列。關於這類方法,使用上也有一些要注意的地方。

  1. 方法中只能有一個可變長度參數,無論資料型態是否相同。
  2. 不能傳遞事先建立好的陣列給該可變長度參數(Java可以)。
  3. 方法參數中有相同型態時,可變長度參數可宣告在任何位置(Java固定在最後一個)。使用方法時,在它後方的其他參數皆需透過指定名稱的方式來傳遞資料。

上一篇:【Kotlin】第3課-迴圈

下一篇:【Kotlin】第5課-集合(List、Set、Map)

留言