集合 (Scala 2.8 - 2.12)

地圖

語言

一個 Map 是一個 Iterable,由成對的鍵和值(也稱為映射關聯)組成。Scala 的 Predef 物件提供了一個隱式轉換,讓您可以撰寫 key -> value 作為成對的 (key, value) 的備用語法。例如,Map("x" -> 24, "y" -> 25, "z" -> 26) 的意思與 Map(("x", 24), ("y", 25), ("z", 26)) 完全相同,但可讀性較佳。

對映射的基本操作類似於對集合的操作。它們總結在以下表格中,並歸類為以下類別

  • 查詢操作 applygetgetOrElsecontainsisDefinedAt。這些將映射轉換為從鍵到值的函數。映射的基本查詢方法為:def get(key): Option[Value]。操作「m get key」測試映射是否包含給定 key 的關聯。如果是,它會在 Some 中傳回關聯的值。如果映射中未定義任何鍵,get 會傳回 None。映射也會定義一個 apply 方法,用於直接傳回與給定鍵關聯的值,而不會將其包裝在 Option 中。如果映射中未定義鍵,則會引發例外。
  • 新增和更新 +++updated,讓您可以將新的繫結新增到映射中或變更現有的繫結。
  • 移除 ---,從映射中移除繫結。
  • 子集合產生器 keyskeySetkeysIteratorvaluesvaluesIterator,以各種形式分別傳回映射的鍵和值。
  • 轉換 filterKeysmapValues,透過過濾和轉換現有映射的繫結,產生新的映射。

Map 類別中的操作

它是什麼 它做了什麼
查詢  
ms get k 映射 ms 中與鍵 k 關聯的值,為選項,如果找不到,則為 None
ms(k) (或寫成 ms apply k) 映射 ms 中與鍵 k 關聯的值,或如果找不到,則為例外。
ms getOrElse (k, d) 映射 ms 中與鍵 k 關聯的值,或如果找不到,則為預設值 d
ms contains k 測試 ms 是否包含鍵 k 的對應。
ms isDefinedAt k contains 相同。
新增和更新  
ms + (k -> v) 包含 ms 的所有對應的映射,以及從鍵 k 到值 v 的對應 k -> v 的映射。
ms + (k -> v, l -> w) 包含所有 ms 對應,以及給定金鑰/值對應的對應。
ms ++ kvs 包含所有 ms 對應,以及所有 kvs 金鑰/值對應的對應。
ms 更新 (k, v) ms + (k -> v) 相同。
移除  
ms - k 包含所有 ms 對應的對應,但排除金鑰 k 的任何對應。
ms - (k, l, m) 包含所有 ms 對應的對應,但排除具有給定金鑰的任何對應。
ms -- ks 包含所有 ms 對應的對應,但排除在 ks 中具有金鑰的任何對應。
子集合  
ms.keys 包含 ms 中每個金鑰的可迭代對象。
ms.keySet 包含 ms 中每個金鑰的集合。
ms.keysIterator 產生 ms 中每個金鑰的迭代器。
ms.values 包含與 ms 中金鑰關聯的每個值的迭代器。
ms.valuesIterator 產生與 ms 中金鑰關聯的每個值的迭代器。
轉換  
ms filterKeys p 僅包含 ms 中滿足謂詞 p 的鍵對應的映射的映射檢視。
ms mapValues f ms 中鍵對應的每個值套用函式 f 所產生的映射檢視。

可變映射還支援下表中總結的運算。

mutable.Map 類別中的運算

它是什麼 它做了什麼
新增和更新  
ms(k) = v (或寫成 ms.update(x, v))。將從鍵 k 到值 v 的映射新增至映射 ms 中,並覆寫 k 的任何先前映射,作為副作用。
ms += (k -> v) 將從鍵 k 到值 v 的映射新增至映射 ms 中,並傳回 ms 本身,作為副作用。
ms += (k -> v, l -> w) 將指定的映射新增至 ms 中,並傳回 ms 本身,作為副作用。
ms ++= kvs kvs 中的所有映射新增至 ms 中,並傳回 ms 本身,作為副作用。
ms put (k, v) 將從鍵 k 到值 v 的映射新增至 ms 中,並傳回先前與 k 關聯的任何值,作為選項。
ms getOrElseUpdate (k, d) 如果地圖 ms 中定義了鍵 k,則傳回其關聯的值。否則,使用對應 k -> d 更新 ms,並傳回 d
移除  
ms -= k 移除地圖 ms 中鍵為 k 的對應,並傳回 ms 本身。
ms -= (k, l, m) 移除地圖 ms 中鍵為給定鍵的對應,並傳回 ms 本身。
ms --= ks 移除地圖 ms 中所有鍵為 ks 的對應,並傳回 ms 本身。
ms remove k 移除地圖 ms 中鍵為 k 的任何對應,並傳回先前與 k 關聯的任何值(作為選項)。
ms retain p 僅保留地圖 ms 中鍵滿足謂詞 p 的對應。
ms.clear() 移除地圖 ms 中的所有對應。
轉換  
ms transform f 使用函式 f 轉換地圖 ms 中所有關聯的值。
複製  
ms.clone 傳回一個新的可變動地圖,其對應與 ms 相同。

地圖的增加和移除操作與集合的類似。與集合一樣,可變動地圖也支援非破壞性的增加操作 +-updated,但它們的使用頻率較低,因為它們涉及可變動地圖的複製。相反地,可變動地圖 m 通常使用兩種變體 m(key) = valuem += (key -> value)「就地」更新。還有變體 m put (key, value),它會傳回一個 Option 值,其中包含先前與 key 關聯的值,或者如果 key 之前不存在於地圖中,則傳回 None

getOrElseUpdate 對於存取作為快取的映射很有用。假設您有一個昂貴的運算,透過呼叫函式 f 觸發

scala> def f(x: String) = {
       println("taking my time."); sleep(100)
       x.reverse }
f: (x: String)String

進一步假設 f 沒有副作用,因此再次使用相同引數呼叫它將永遠產生相同的結果。在這種情況下,您可以透過將引數和 f 結果的先前運算結果儲存在地圖中,並僅在未在那裡找到引數結果時才運算 f 的結果來節省時間。可以說,這個地圖是函式 f 的運算的「快取」。

scala> val cache = collection.mutable.Map[String, String]()
cache: scala.collection.mutable.Map[String,String] = Map()

現在您可以建立f 函式的更有效率快取版本

scala> def cachedF(s: String) = cache.getOrElseUpdate(s, f(s))
cachedF: (s: String)String
scala> cachedF("abc")
taking my time.
res3: String = cba
scala> cachedF("abc")
res4: String = cba

請注意,getOrElseUpdate 的第二個引數是「依名稱」,因此只有在 getOrElseUpdate 需要其第二個引數的值(也就是當其第一個引數在 cache 地圖中找不到時)才會執行上述 f("abc") 的運算。您也可以直接使用基本的地圖操作來實作 cachedF,但這樣會需要更多的程式碼

def cachedF(arg: String) = cache get arg match {
  case Some(result) => result
  case None =>
    val result = f(x)
    cache(arg) = result
    result
}

同步化集合和地圖

若要取得執行緒安全的可變動地圖,您可以將 SynchronizedMap 特質混合到任何您想要的特定地圖實作中。例如,您可以將 SynchronizedMap 混合到 HashMap 中,如下列程式碼所示。此範例首先匯入兩個特質(MapSynchronizedMap)和一個類別(HashMap)來自套件 scala.collection.mutable。範例的其餘部分是單例物件 MapMaker 的定義,其中宣告一個方法 makeMapmakeMap 方法宣告其結果類型為字串金鑰對應到字串值的變動地圖。

  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?"
        }
    }
  }
混合 `SynchronizedMap` 特質。

makeMap 主體內的第 1 個陳述式會建構一個新的變動 HashMap,其中混合了 SynchronizedMap 特質

new HashMap[String, String] with
  SynchronizedMap[String, String]

給定此程式碼,Scala 編譯器會產生 HashMap 的合成子類別,將 SynchronizedMap 混入,並建立(並傳回)其執行個體。此合成類別也會覆寫名為 default 的方法,這是因為此程式碼

override def default(key: String) =
  "Why do you want to know?"

如果您要求地圖提供特定金鑰的值,但它沒有該金鑰的對應,您預設會取得 NoSuchElementException。但是,如果您定義新的地圖類別並覆寫 default 方法,您的新地圖會在使用不存在的金鑰查詢時傳回 default 傳回的值。因此,編譯器從同步地圖程式碼中的程式碼產生的合成 HashMap 子類別會傳回有點簡短的回應字串,"Why do you want to know?",當使用不存在的金鑰查詢時。

由於 makeMap 方法傳回的可變動地圖會混入 SynchronizedMap 特質,因此它可以同時由多個執行緒使用。對地圖的每個存取都會同步化。以下是地圖在解譯器中由一個執行緒使用的範例

scala> val capital = MapMaker.makeMap  
capital: scala.collection.mutable.Map[String,String] = Map()
scala> capital ++ List("US" -> "Washington",
        "France" -> "Paris", "Japan" -> "Tokyo")
res0: scala.collection.mutable.Map[String,String] =
  Map(France -> Paris, US -> Washington, Japan -> Tokyo)
scala> capital("Japan")
res1: String = Tokyo
scala> capital("New Zealand")
res2: String = Why do you want to know?
scala> capital += ("New Zealand" -> "Wellington")
scala> capital("New Zealand")                    
res3: String = Wellington

您可以用類似於建立同步地圖的方式建立同步集合。例如,您可以透過混入 SynchronizedSet 特質,建立同步 HashSet,如下所示

import scala.collection.mutable
val synchroSet =
  new mutable.HashSet[Int] with
      mutable.SynchronizedSet[Int]

最後,如果您正在考慮使用同步化集合,您可能也希望考慮使用 java.util.concurrent 的並行集合。

此頁面的貢獻者