默認情況下在使用“Set”或“Map”的時候,獲得的都是不可變對象。如果需要的是可變版本,需要先寫明引用。
如果同一個源文件中既要用到可變版本,也要用到不可變版本的集合或映射,方法之一是引用包含了可變版本的包名:
scala> import scala.collection.mutable import scala.collection.mutable
與以往一樣, 不可變集可以用Set指代,但現在還可以用mutable.Set指代可變集。舉例如下:
scala> val mutaSet = mutable.Set(1, 2, 3)
mutaSet: scala.collection.mutable.Set[Int] = Set(1, 2, 3)
使用集
集的關鍵特性在於它可以對使用對象的==操作檢查,確保在任何時候每個對象只在集中保留最多一個副本。例如,我們可以用集對字符串不同單詞計數。
如果指定空格和標點符號為單詞的分隔符,String的split方法可以把字符串分割成單詞。正則表達式“[ ! , .]“足以完成該功能:它表明字符串應該在每個存在一到多個空格或標點符號的地方分割開來:
scala> val text = "See Spot run. Run, Spot. Run!" text: String = See Spot run. Run, Spot. Run! scala> val wordsArray = text.split("[ !,.]+") wordsArray: Array[String] = Array(See, Spot, run, Run, Spot, Run)
要對不同的單詞計數,可以先把它們變成同樣的大小寫然后加到集中。由於集不能重復添加,因此不同的單詞只在集中出現一次。首先,可以使用Set伴生對象提供的empty方法創建空集:
scala> val words = mutable.Set.empty[String]
words: scala.collection.mutable.Set[String] = Set()
然后,用for表達式枚舉所有的單詞,分別把它們轉換為小寫字母形式,然后使用+=操作符添加到可變集中:
scala> for (word <- wordsArray) words += word.toLowerCase scala> words res2: scala.collection.mutable.Set[String] = Set(see, run, spot)
使用映射
映射可以用來把值與集合中的元素聯系起來。映射的使用與數組接近,只是你可以用任何類型的鍵,而不僅僅是使用從0開始計數的整數做索引。如果引用了scala.collection.mutable包,你還可以創建空的可變映射:
scala> val map = mutable.Map.empty[String, Int]
map: scala.collection.mutable.Map[String,Int] = Map()
映射中的條目設置與數組中的類似:
scala> map("hello") = 1 scala> map("there") = 2 scala> map res5: scala.collection.mutable.Map[String,Int] = Map(hello -> 1, there -> 2)
類似的,讀取映射和讀取數組也是一樣的:
scala> map("hello")
res6: Int = 1
把上面的例子放在一起,就有了下面對字符串中單詞出現次數做計數的方法:
scala> def countWords(text: String) = { | val counts = mutable.Map.empty[String, Int] | for (rawWord <- text.split("[ ,!.]+")) { | val word = rawWord.toLowerCase | val oldCount = | if (counts.contains(word)) counts(word) | else 0 | counts += (word -> (oldCount + 1)) | } | counts | } countWords: (text: String)scala.collection.mutable.Map[String,Int] scala> countWords("See Spot run! Run, Spot. Run!") res7: scala.collection.mutable.Map[String,Int] = Map(spot -> 2, see -> 1, run -> 3)
有序的集和映射
scala的集合庫提供了SortedSet和SortedMap特質。這兩個特質分別由類TreeSet和TreeMap實現,他們都使用了紅黑樹有序地保存元素(TreeSet類)或鍵(TreeMap類)。具體的順序取決於Ordered特質,集的元素類型或映射的鍵類型必須要么混入,要么能夠隱式地轉換成Ordered特質。這些類只有不可變類型的版本。下面是TreeSet的例子:
scala> import scala.collection.immutable.TreeSet import scala.collection.immutable.TreeSet scala> val ts = TreeSet(9, 3, 1, 8, 0, 2, 7, 4, 6, 5) ts: scala.collection.immutable.TreeSet[Int] = TreeSet(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) scala> val cs = TreeSet('f', 'u', 'n') cs: scala.collection.immutable.TreeSet[Char] = TreeSet(f, n, u)
下面是TreeMap的例子:
scala> import scala.collection.immutable.TreeMap import scala.collection.immutable.TreeMap scala> var tm = TreeMap(3 -> 'x', 1 -> 'x', 4 -> 'x') tm: scala.collection.immutable.TreeMap[Int,Char] = Map(1 -> x, 3 -> x, 4 -> x) scala> tm += (2 -> 'x') scala> tm res9: scala.collection.immutable.TreeMap[Int,Char] = Map(1 -> x, 2 -> x, 3 -> x, 4 -> x)
同步的(Synchronized)集和映射
如果需要線程安全的映射,可以把SynchronizedMap特質混入到你想要的特質類實現中。下面是一個吧SynchronizedMap混入HashMap的例子:
import scala.collection.mutable.{Map, SynchronizedMap, HashMap} object MapMaker { def makeMap: Map[String, String] = { new HashMap[String, String] with SynchronizedMap[String, String]{ override def default(key: String) = "Why do you want to know?" } } }
如果你請求映射返回與特定鍵關聯的值,而該鍵的映射實際不存在,默認你將得到NoSuchElementException。然而如果你定義了新的映射類並重載了default方法,那么這個新的映射將在查詢不存在的鍵時返回default方法的返回值。根據上面的代碼,由編譯器構造的合成HashMap子類將在查詢不存在的鍵時返回”Why do you want to know?"。
下面的例子演示了在解釋器中,單線程訪問映射的情況:
scala> import scala.collection.mutable.{Map, SynchronizedMap, HashMap} import scala.collection.mutable.{Map, SynchronizedMap, HashMap} scala> object MapMaker { | def makeMap: Map[String, String] = { | new HashMap[String, String] with SynchronizedMap[String, String]{ | override def default(key: String) = | "Why do you want to know?" | } | } | } warning: there was one deprecation warning; re-run with -deprecation for details defined object MapMaker scala> val capital = MapMaker.makeMap capital: scala.collection.mutable.Map[String,String] = Map() scala> capital ++ List("US" -> "Washington", "Paris" -> "France", "Japan" -> "To kyo") res10: scala.collection.mutable.Map[String,String] = Map(Paris -> France, Japan -> Tokyo, US -> Washington) scala> capital("Japan") res11: String = Why do you want to know? scala> capital += ("New Zealand" -> "Wellington") res12: capital.type = Map(New Zealand -> Wellington) scala> capital("New Zealand") res13: String = Wellington
你可以按照與創建同步映射類似的方法創建同步集。例如可以通過混入SynchronizedSet特質創建同步的HashSet,見下:
scala> import scala.collection.mutable import scala.collection.mutable scala> val synchroSet = | new mutable.HashSet[Int] with | mutable.SynchronizedSet[Int] warning: there was one deprecation warning; re-run with -deprecation for details synchroSet: scala.collection.mutable.HashSet[Int] with scala.collection.mutable.SynchronizedSet[Int] = Set()