Set 與 Map


Set無序集合物件,物件所收集的元素不重複。在Scala中,預設的Set實作是不可變動的(Immutable),如果你要建立Set實例,以下是個例子:
val names1 = Set("Justin", "caterpillar", "momor")
names1.foreach(println) // 逐行顯示Justin、caterpillar、momor

val names2 = names1 + "caterpillar"
names2.foreach(println) // 逐行顯示Justin、caterpillar、momor

val names3 = names1 + "hamimi"
names3.foreach(println) // 逐行顯示Justin、caterpillar、momor、hamimi

+ 可用來於集合中新增元素,由於預設的Set實作是不可變動的,所以實際上會建立一個新的Set物件傳回,但由於Set中的元素不重複,如果要加入的元素是 重複的,就會直接傳回原來的Set實例,因此範例中names2中實際上並沒有增加元素,而且直接傳回names1的實例。

要從元素中刪 除某個元素,可以使用-方法。如果要串接兩個Set,則可以使用++(也就是聯集操作,因為Set不允許重複),如果要從某個Set中,刪去另一個Set 物件所指定的元素,則可以使用--,如果要作交集操作,則使用**,如果要比較兩個Set物件的內含元素是否相同,可以直接使用==。以下是直接從直譯環 境作個示範:
scala> val x = Set(1, 2, 3, 4, 5)
x: scala.collection.immutable.Set[Int] = Set(5, 3, 1, 4, 2)

scala> x - 2
res3: scala.collection.immutable.Set[Int] = Set(5, 3, 1, 4)

scala> x ++ Set(6, 7, 8, 9)
res4: scala.collection.immutable.Set[Int] = Set(5, 1, 6, 9, 2, 7, 3, 8, 4)

scala> x -- Set(2, 3)
res5: scala.collection.immutable.Set[Int] = Set(5, 1, 4)

scala> x ** Set(3, 4, 5, 6, 7)
res6: scala.collection.immutable.Set[Int] = Set(5, 3, 4)

scala> x == Set(1, 2, 3, 4, 5)
res7: Boolean = true

scala>


剛剛提過,預設的Set實作是不可變動的,實際上,預設是使用 scala.collection.immutable.Set 的實作(事實上,不可變動的群集定義都是位於 scala.collection.immutable 套件之中),如果你需要可變動的Set,則可以匯入(import)scala.collection.mutable.Set(可變動的群集定義都是位於 scala.collection.mutable 套件之中)。例如:
import scala.collection.mutable.Set

val names = Set("Justin", "caterpillar", "momor")
names.foreach(println)

names += "hamimi"
names.foreach(println) // 逐行顯示Justin、caterpillar、momor、hamimi

在上例中,呼叫了names的+=方法,由於你匯入了scala.collection.mutable.Set,所以使用的是可變動的Set,+=是該Set實作物件上的方法,這會將元素加入同一個Set實例,而不是建立新的Set實例。

事實上,先前未匯入scala.collection.mutable.Set時,你程式中所使用的Set名稱所參考的物件,其實是定義在scala.Predef中:
package scala
object Predef {
    ...
    type Map[A, B] = collection.immutable.Map[A, B]
    type Set[A] = collection.immutable.Set[A]

    val Map = collection.immutable.Map
    val Set = collection.immutable.Set
    ...
}

val宣告了Map與Set分別參考至collection.immutable.Set與collection.immutable.Map物件,而type為collection.immutable.Set[A]與collection.immutable.Map[A, B]建立了別名Set[A]與Map[A, B]。

Map是具有鍵/值(Key/Value)的字典物件。在Scala中預設的Map實作是不可變動的,理由在上面的程式碼片段可以看到,預設的Map是 collection.immutable.Map 的實作。要建立Map物件,以下是個範例:
val rooms = Map(101 -> "Justin", 102 -> "caterpillar")
println(rooms(101)) // 顯示 Justin
println(rooms(102)) // 顯示 caterpillar

->左 邊指定鍵,右邊是值。指定鍵要取回值時,是使用(),從先前的介紹你大概也可以推論出,rooms(101)這樣的指定時,其實會轉換為呼叫 rooms.apply(101)這樣的呼叫。由於預設的Map是不可變動的,所以如下要更新Map中的元素,實際上會傳回新的Map物件,例如:
val rooms = Map(101 -> "Justin", 102 -> "caterpillar")

// 下面真正是變成 val newRooms = rooms.apply(101, "Hamimi")
val newRooms = rooms(101) = "Hamimi"
println(rooms(101)) // 顯示 Justin
println(newRooms(101)) // 顯示 Hamimi

範 例中第二行程式碼其實是很不直覺,因為=運算應該是沒有傳回值的(也就是結果是Unit),不過事實上如註解中所說明的,rooms(101) = "Hamimi"實際上會變成rooms.update(101, "Hamimi"),而update方法傳回的是新的Map物件。

同樣的,如果你要使用可變動的Map實作,則可以匯入 collection.mutable.Map,例如:
import collection.mutable.Map

val rooms = Map[String, String]()

rooms("101") = "Justin"
println(rooms("101"))

rooms += ("102" -> "caterpillar")
rooms.foreach(println)

這個例子同時示範了如何新增鍵/值元素至Map中,也就是使用("102" -> "caterpillar")這樣的寫法,另外也示範了,在呼叫Map的foreach方法時,每次foreach會取得 Tuple 物件,代表了該次迭代時的鍵/值,這個例子會顯示以下的結果:
Justin
(101,Justin)
(102,caterpillar)


下面這個例子則示範如何使用傳入的 Tuple:
import collection.mutable.Map
val rooms = Map[String, String]()

rooms("101") = "Justin"

rooms += ("102" -> "caterpillar")
rooms.foreach(element => {
val (key, value) = element
println(key + ": " + value)
})

這個例子會顯示以下的結果:
101: Justin
102: caterpillar