Maps
英文原文:http://docs.scala-lang.org/overviews/collections/maps.html
Map 是一个由键值对组成的可迭代(Iterable 类的实例)的集合(又称为映射或者关联)。Scala 的 Predef 类提供了一个隐示转换使得你可以用 key -> value 这样的语法来表示键值对 (key, value)。例如,Map("x"->24, "y"->25, "z"->26) 等同于 Map(("x", 24), ("y", 25), ("z", 26)),但是前者可读性更佳。
Map 上的基本操作与 Set 上的类似。后面的表格中对它的基本操作进行了总结,其主要可以划分为几大类:
- Lookup 类操作,apply、get、getOrElse、contains 以及 isDefinedAt。这些方法将 Map 集合转化为从键到值的偏函数。Map 基本的查找方法是:def get(key): Option[Value]。"m get key" 操作可以测试 Map 类集合中是否含有指定 key 的一个映射。如果包含,将以 Some 的形式返回该键对应的值,否则返回 None。Map 类集合还定义了 apply 方法,此方法直接返回 key 对应的值,而不是用 Option 类包裹起来再返回。如果指定 key 没有在 Map 中定义,则将会有异常抛出。
- Additions and Updates 类操作,+、++ 以及 updated,用于添加新的键值对到 Map 中或者是更新已有键值对。
- Removals 类操作,- 和 --,用于删除 Map 中的键值对。
- Subcollection producers 类操作,keys、keySet、keysIterator、values 以及 valuesIterator,用于以不同的形式分别返回 Map 中的键或者值。
- Transformations 类操作,filterKeys 和 mapValues,用于过滤 Map 中的元素以及转化 map 中的元素。
Map 类中的方法
方法 | 作用 |
Lookups: | |
ms get k | 以 Option 类包裹的方式返回 ms 中键 k 对应的值,如果不存在则返回 None |
ms(k) | (也可以写成 ms apply k)返回 ms 中键 k 对应的值,如果不存在则抛出异常 |
ms getOrElse (k, d) | 返回 ms 中键 k 对应的值,如果不存在则返回默认值 d |
ms contains k | 判断 ms 中是否包含键 k 对应的一个映射对 |
ms isDefiniedAt k | 等同于 ms contains k |
Additions and Update: | |
ms + (k->v) | 返回一个新的 Map,包含原集合中所有元素以及给定 k->v 键值对 |
ms + (k->v, l->w) | 返回一个新的 Map,包含原集合中所有元素以及所有给定键值对 |
ms ++ kvs | 返回一个新的 Map,包含原集合中所有元素以及 kvs 中所有元素 |
ms updated (k, v) | 等同于 ms + (k->v) |
Removals: | |
ms - k | 返回一个新的 Map,包含原集合中除给定 k->v 键值对外的所有元素 |
ms - (k, l, m) | 返回一个新的 Map,包含原集合中除所有给定键值对外的所有元素 |
ms -- ks | 返回一个新的 Map,包含原集合中除 ks 中所包含的键对应的键值对外的所有元素 |
Subcollections: | |
ms.keys | 返回一个包含 ms 中所有键的 Iterable 类集合 |
ms.keySet | 返回一个包含 ms 中所有键的 Set 类集合 |
ms.KeyIterator | 返回一个依次产生 ms 中键的迭代器 |
ms.values | 返回一个依次产生 ms 中键对应的值的 Iterable 类实例 |
ms.valuesIterator | 返回一个依次产生 ms 中键对应的值的迭代器 |
Transformations: | |
ms filterKeys p | 返回 ms 中键满足谓词条件 p 的所有元素组成的一个新的 Map |
ms mapValues f | 返回一个新的 Map,其中键与原集合相同,键对应的值为对原来值应用函数 f 的结果 |
可变 Map 在此基础上还添加了一些操作,总结如下表。
方法 | 作用 |
Additons and Updates: | |
ms(k) = v | (也可以写成 ms.update(x, v))。 以副作用的方式添加从键 k 到值 v 的映射, 覆盖键 k 原来的值 |
ms += (k->v) | 以副作用的方式添加从键 k 到值 v 的映射, 如果原来存在,则覆盖键 k 原来的值 |
ms += (k->v, l->w) | 以副作用的方式添加给定键值对, 如果给定键原来存在,则覆盖该键对应的值 |
ms ++= kvs | 以副作用的方式添加 kvs 中所有键值对, 如果存在相同键则覆盖其对应的值 |
ms put (k, v) | 添加键值对 k->v 到 ms 中, 并以 Option 包裹的形式返回原来该键对应的值, 如果原来不存在该键则返回 None |
ms getOrElseUpdate | 如果键 k 在 ms 中有定义,则返回其对应的值; 否则,用键值对 k->d 更新该键对应的值并返回新的值 d |
Removals: | |
ms -= k | 以副作用的方式从 ms 中删除给定键 k 对应的键值对,并返回 ms 本身 |
ms -= (k, l, m) | 以副作用的方式从 ms 中删除所有给定键对应的键值对,并返回 ms 本身 |
ms --= ks | 以副作用的方式从 ms 中删除集合 ks 中所包含的键对应的键值对, 并返回 ms 本身 |
ms remove k | 以副作用的方式从 ms 中删除给定键 k 对应的键值对, 并以 Option 包裹的方式返回键 k 对应的值, 如果原来就没有定键 k,则返回 None |
ms retain p | 只保留 ms 中键满足谓词条件 p 的键值对, 以副作用的方式删除掉其他元素 |
ms.clear() | 删除 ms 中所有的元素 |
Transformation: | |
ms transform f | 返回一个新的 Map,其中键同原来集合,键对应的值为对键和值应用二元函数 f |
Cloning: | |
ms.clone | 返回一个新的 Map,元素同原来 Map |
Map 的增加和删除操作与 Set 类似。如 Set 一样,可变的 Map 也提供了无副作用的增加、删除和更新操作:+、-、updated,但是这在可变集里面用的很少,因为这些操作涉及到重新拷贝一份 Map 数据。取而代之的是,可变 Map 总是使用 m(key) = value 和 m += (key->value)
这两个方法变体对集合进行“就地”更新。除了这两个方法,还有一个方法变体 m put (key, value),它除了添加给定键值对外,还返回一个用 Option 类包裹起来的原来键 key 对应的值,若原来键 key 不存在则返回 None。
getOrElseUpdate 方法对作为缓存使用的 Map 的访问是非常有用的。比如说你定义了一个
计算开销很大的函数 f:
scala> def f(x: String) = {
println("taking my time."); sleep(100)
x.reverse }
f: (x: String) String
scala> val cache = collection.mutable.Map[String, String]()
cache: scala.collection.mutable.Map[String, String] = Map()
你现在可以创建一个更高效的带缓存版本的函数 f:
def cachedF(arg: String) = cache get arg match {
case Some(result) => result
case None =>
val result = f(x)
cache(arg) = result
result
}
同步 Sets 和 Maps
你可以通过将 SynchronizedMap 特质混入到你使用的 Map 具体实现类上来获得一个线程安全的可变 Map。例如,可以将 SynchronizedMap 特质混入到 HashMap,如下面代码所示。这个例子开头导入了两个特质:Map 和 SynchronizedMap,一个包 scala.collection.mutable 中的类:HashMap。例子中的剩余部分定义了一个单例对象 MapMaker,其中定义了一个 makeMap 方法。这个 makeMap 方法声明了返回值类型为一个从 String 键到 String 值的可变 Map。
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 方法中的第一个语句构造了一个混入了 SynchronizedMap 特质的可变 HashMap:
new HashMap[String, String] with SynchronizedMap[String, String]
根据这行代码,scala 编译器将生成一个混入了 SynchronizedMap 特质的 HashMap 的子类,然后创建一个该生成类的实例并返回。这个混合类还重写了一个 default 方法,这可以根据以下代码得知:
override def default(key: String) = "Why do you want to know?"
如果你在一个 Map 中查询一个给定的 key,当这个 key 不存在的时候将会默认抛出一个 NoSuchElementException 异常。但是,如果你定义了一个 Map 并重写了它的 default 方法,则当你查询的 key 不存在的时候,将会返回 default 方法的返回值。因此,当你在以上同步 Map 的例子中去访问一个不存在的 key 的时候,这个编译器生成的 HashMap 的子类会返回一个简短的响应字符串“Why do you want to know?”。
因为这个由 makeMap 方法返回的可变 Map 混入了 SynchronizedMap 特质,所以它可以同时被多个线程使用。对这个可变 Map 的访问是同步的。下面是一个解释器中运行的在一个线程中使用这个同步 Map 的例子:
scala> val capital = MapMaker.makeMap
capital: scala.collection.mutable.Map[String, String] = Map()
scala> capital ++ List("US" -> "Washington", "Paris" -> "France", "Japan" -> "Tokyo")
res0: scala.collection.mutable.Map[String, String] = Map(Paris -> France, US -> Washington, Japan -> Tokyo)
scala> captial("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
你可以用类似的方法创建一个同步的 Set。例如,你可以创建一个同步的 HashSet,只要混入 SynchronizedSet 特质,就像下面这样:
import scala.collection.mutable
val synchroSet = new mutable.HashSet[Int] with mutable.SynchronizedSet[Int]
最后,如果你想使用同步集合,还可以考虑包 java.util.concurrent 中的同步集合类。