过时通知
映射 是一个 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 |
将函数 f 应用于与 ms 中的键关联的每个值而产生的地图视图。 |
可变映射还支持下表中总结的操作。
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) |
如果键 k 在映射 ms 中已定义,则返回其关联的值。否则,使用映射 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
中,如下面的代码所示。此示例从包 scala.collection.mutable
中导入两个特征 Map
和 SynchronizedMap
,以及一个类 HashMap
开始。示例的其余部分是单例对象 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
主体内的第一条语句构造了一个新的可变 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
的并发集合。