Array


在Scala中,如果要建立陣列,可以使用Array類別,建立陣列時,必須指定陣列後所存放的元素型態。例如以下建立長度為5,內含Int、Double、Boolean、String等指定型態的陣列:
scala> val arr1 = new Array[Int](5)
arr1: Array[Int] = Array(0, 0, 0, 0, 0)

scala> val arr2 = new Array[Double](5)
arr2: Array[Double] = Array(0.0, 0.0, 0.0, 0.0, 0.0)

scala> val arr3 = new Array[Char](5)
arr3: Array[Char] = Array( ,  ,  ,  ,  )

scala> val arr4 = new Array[Boolean](5)
arr4: Array[Boolean] = Array(false, false, false, false, false)

scala> val arr5 = new Array[String](5)
arr5: Array[String] = Array(null, null, null, null, null)

scala> 

在建立陣列之後,如果內含元素是整數,則預設值為0,浮點數則預設值為0.0,字元則預設值為空字元(字元編碼為0的字元),布林值預設為false,其它型態則預設值為null(與Java的陣列類似)。

如果要多維陣列,基本上是以一維陣列來摸擬,例如要建立二維陣列:
scala> val arr = new Array[Array[Int]](5, 5)
arr: Array[Array[Int]] = Array(Array(0, 0, 0, 0, 0), Array(0, 0, 0, 0, 0), Array(0, 0, 0, 0, 0), Array(0, 0, 0, 0, 0), Array(0, 0, 0, 0, 0))

scala>

如果要存取陣列,必須指定陣列索引,與其它語言存取陣列慣例不同的是,在Scala中,指定陣列索引時是使用()而非[],例如:
val arr = new Array[Int](3)
arr(0) = 10
arr(1) = 20
arr(2) = 30
println(arr(0)) // 顯示 10
println(arr(1)) // 顯示 20
println(arr(2)) // 顯示 30

事實上,當你使用arr(0) = 10這樣的指定方式時,Scala會將之轉換為呼叫陣列的update()方法,而你用arr(0)的方式要取得指定索引的元素時,Scala會將之轉換為呼叫apply方法。以下是個簡單的驗證方式:
val arr = new Array[Int](3)
arr.update(0, 10)
arr.update(1, 20)
arr.update(2, 30)
println(arr.apply(0)) // 顯示 10
println(arr.apply(1)) // 顯示 20
println(arr.apply(2)) // 顯示 30

在Scala中,指定索引進行存取時使用()而不是[],其實是為了語法的一致性。這種在()中指定索引進行存取的方式,並非只有陣列專屬,只要是有提供update()或apply()方法的物件,其實都可以使用這樣的存取方式,像是在 String 也曾經使用過這樣的方式:
val str = "Scala"
println(str(0)) // 顯示 'S'
println(str(1)) // 顯示 'c'
println(str(2)) // 顯示 'a'
println(str(3)) // 顯示 'l'
println(str(4)) // 顯示 'a'

如果你要在建立陣列時,一併指定元素值,則可以直接使用以下的語法:
val arr = Array(10, 20, 30)
for(i <- 0 until arr.length) {
println(arr(i))
}

其實在 單例物件 中有介紹過了,Array(10, 20, 30)這樣的寫法是個語法蜜糖,Scala會將之轉換為呼叫 scala.Array 伴侶物件的apply方法,這個方法會傳回含指定元素的陣列。以下這個程式可以驗證這個說法:
val arr = Array.apply(10, 20, 30)
for(i <- arr) {
println(i)
}

上面這個範例程式,同時示範了迭代陣列的另一種方式,每一次迭代,都會將arr的元素取出指定給i。事實上,陣列提供foreach方法可以使用,上面的程式你可以這麼撰寫:
val arr = Array(10, 20, 30)
arr.foreach((i: Int) => println(i))

簡單的函式 中有介紹過,foreach中是函式字面寫法,執行時期會傳入函式物件,每次取出一個元素,就會呼叫函式物件。你可以利用類型推斷,讓這個範例更簡短一些:
val arr = Array(10, 20, 30)
arr.foreach(i => println(i))

arr.foreach(i => println(i))其實是arr.foreach((i) => println(i))簡寫,因為只有一個參數,所以括號可以省略。事實上,就上面這個例子來說,最簡短的寫法是:
val arr = Array(10, 20, 30)
arr.foreach(println)

事實上,foreach中是 部份套用函式(Partially applied function)的寫法,這之後還會詳述。

像foreach這種傳入函式物件執行某個操作的作法,在Scala相當常見。例如陣列的filter方法,可以傳入一個會運算出布林值的函式物件,根據真或假決定結果要包括哪些元素,例如:
val arr = Array(10, 20, 30, 40, 50, 60)
arr.filter(x => x > 30).foreach(println)

事實上,上面這個程式可以使用佔位字元語法(Placeholder syntax)撰寫為以下的方式:
val arr = Array(10, 20, 30, 40, 50, 60)
arr.filter(_ > 30).foreach(println) // 逐行顯示 40、50、60

_ 作為佔位字元,就目前來說,你可以將看它看作是填充欄位,每一次取得的元素值將值入這個欄位(其實完整的語法應該是(_ : Int) > 30,不過由於利用了類型推斷,所以可以推斷出_應該是Int,因此可以寫為_ > 30,佔位字元語言之後還會詳述)。

類似地,如果你不僅是想過濾,還想對元素作某些運算後傳回的話,則可以使用map方法,例如將每個元素加1後傳回新陣列:
val arr = Array(10, 20, 30)
arr.map(_ + 1).foreach(println) // 逐行顯示 11、21、31

上例中直接使用了佔位字元語法,如果要寫的詳細的話,其實是要寫為:
val arr = Array(10, 20, 30)
arr.map(x => x + 1).foreach(println) // 逐行顯示 11、21、31

陣列可以串接,不過不是使用+,而是使用++(這是Array上定義的方法),這會將兩個陣列的元素取出,建立一個新陣列後傳回。例如:
val arr1 = Array(10, 20, 30)
val arr2 = Array(40, 50, 60)
(arr1 ++ arr2).foreach(println) // 逐行顯示 10、20、30、40、50、60

如果你要比較兩個陣列的內含值是否相同,由於要逐一比對陣列中每個陣列的元素物件內含值,所以不能直接使用==比較,而要使用deepEquals方法。例如:
val arr1 = Array(10, 20, 30)
val arr2 = Array(10, 20, 30)
println(arr1 == arr2) // 顯示 false
println(arr1 deepEquals arr2) // 顯示 true