集合

地圖

語言

一個 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.keys 包含 ms 中每個鍵的可迭代物件。
ms.keySet 包含 ms 中每個鍵的集合。
ms.keysIterator 產生 ms 中每個鍵的迭代器。
ms.values 包含與 ms 中鍵關聯的每個值的迭代器。
ms.valuesIterator 一個迭代器,產生與 ms 中的每個鍵值關聯的值。
轉換  
ms.view.filterKeys(p) 一個只包含 ms 中鍵值滿足謂詞 p 的對應的映射檢視。
ms.view.mapValues(f) 一個映射檢視,將函式 f 套用於 ms 中與每個鍵值關聯的值。

不可變映射除了支援透過傳回新的 Map 來新增和移除對應的運算外,還支援以下表格中摘要的運算。

immutable.Map 類別中的運算

是什麼 作用
新增和更新  
ms.updated(k, v)
ms + (k -> v)
包含 ms 的所有對應,以及從鍵值 k 到值 v 的對應 k -> v 的映射。
移除  
ms.remove(k)
ms - k
包含 ms 的所有對應,但排除任何鍵值為 k 的對應。
ms.removeAll(ks)
ms -- ks
包含 ms 的所有對應,但排除任何鍵值在 ks 中的對應。

可變映射除了支援以下表格中摘要的運算外,還支援以下運算。

mutable.Map 類別中的運算

是什麼 作用
新增和更新  
ms(k) = v (或寫成 ms.update(k, v))。將鍵 k 對應到值 v 的對應關係新增到映射 ms 中,作為副作用,覆寫之前任何 k 的對應關係。
ms.addOne(k -> v)
ms += (k -> v)
將鍵 k 對應到值 v 的對應關係新增到映射 ms 中,作為副作用,並傳回 ms 本身。
ms.addAll(xvs)
ms ++= kvs
kvs 中的所有對應關係新增到 ms 中,作為副作用,並傳回 ms 本身。
ms.put(k, v) 將鍵 k 對應到值 v 的對應關係新增到 ms 中,並傳回之前與 k 關聯的任何值(作為選項)。
ms.getOrElseUpdate(k, d) 如果鍵 k 在映射 ms 中有定義,傳回其關聯的值。否則,使用對應關係 k -> d 更新 ms,並傳回 d
移除  
ms.subtractOne(k)
ms -= k
從 ms 中移除鍵 k 的對應關係,作為副作用,並傳回 ms 本身。
ms.subtractAll(ks)
ms --= ks
移除 ks 中所有鍵於 ms 中,並回傳 ms 本身。
ms.remove(k) 移除 ms 中任何鍵為 k 的對應,並回傳先前與 k 相關聯的任何值(以選項型態)。
ms.filterInPlace(p) 僅保留 ms 中鍵滿足謂詞 p 的對應。
ms.clear() 移除 ms 中所有對應。
轉換  
ms.mapValuesInPlace(f) 使用函式 f 轉換映射 ms 中所有相關聯的值。
複製  
ms.clone 回傳一個新的可變映射,其對應與 ms 相同。

映射的加入和移除操作反映了集合的操作。可變映射 m 通常會就地更新,使用兩種變體 m(key) = valuem += (key -> value)。還有變體 m.put(key, value),它會回傳一個 Option 值,其中包含先前與 key 相關聯的值,或 None(如果 key 之前不存在於映射中)。

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

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

def 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): 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 需要其第二個參數的值時,才會執行 f("abc") 的運算,也就是當其第一個參數未在 cache 映射中找到時。您也可以直接使用基本映射操作來實作 cachedF,但這樣會需要更多程式碼

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

此頁面的貢獻者