過時公告
一個 Map 是一個 Iterable,由成對的鍵和值(也稱為映射或關聯)組成。Scala 的 Predef 物件提供了一個隱式轉換,讓您可以撰寫 key -> value
作為成對的 (key, value)
的備用語法。例如,Map("x" -> 24, "y" -> 25, "z" -> 26)
的意思與 Map(("x", 24), ("y", 25), ("z", 26))
完全相同,但可讀性較佳。
對映射的基本操作類似於對集合的操作。它們總結在以下表格中,並歸類為以下類別
- 查詢操作
apply
、get
、getOrElse
、contains
和isDefinedAt
。這些將映射轉換為從鍵到值的函數。映射的基本查詢方法為:def get(key): Option[Value]
。操作「m get key
」測試映射是否包含給定key
的關聯。如果是,它會在Some
中傳回關聯的值。如果映射中未定義任何鍵,get
會傳回None
。映射也會定義一個apply
方法,用於直接傳回與給定鍵關聯的值,而不會將其包裝在Option
中。如果映射中未定義鍵,則會引發例外。 - 新增和更新
+
、++
、updated
,讓您可以將新的繫結新增到映射中或變更現有的繫結。 - 移除
-
、--
,從映射中移除繫結。 - 子集合產生器
keys
、keySet
、keysIterator
、values
、valuesIterator
,以各種形式分別傳回映射的鍵和值。 - 轉換
filterKeys
和mapValues
,透過過濾和轉換現有映射的繫結,產生新的映射。
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) = value
或 m += (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
中,如下列程式碼所示。此範例首先匯入兩個特質(Map
和 SynchronizedMap
)和一個類別(HashMap
)來自套件 scala.collection.mutable
。範例的其餘部分是單例物件 MapMaker
的定義,其中宣告一個方法 makeMap
。makeMap
方法宣告其結果類型為字串金鑰對應到字串值的變動地圖。
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?"
}
}
}
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
的並行集合。