您偶然发现了 ScalaMap
实现的一个棘手功能。您缺少的问题是它mapValues
实际上并没有返回 a new Map
:它返回 aview
的 a Map
。换句话说,它以这样一种方式包装您的原始地图,即每当您访问一个值时,它都会.toUpperCase
在将值返回给您之前进行计算。
这种行为的好处是 Scala 不会为未访问的值计算函数,也不会花时间将所有数据复制到新的Map
. 缺点是每次访问该值时都会重新计算该函数。因此,如果您多次访问相同的值,您最终可能会进行额外的计算。
那么为什么不SortedMap
返回 aSortedMap
呢?因为它实际上返回了一个Map
-wrapper。底层的Map
,然后是被包装的,仍然是 a SortedMap
,所以如果你要遍历它,它仍然是排序的。你我都知道,但类型检查器不知道。看起来他们当然可以以仍然保持SortedMap
特征的方式编写它,但他们没有。
您可以在代码中看到它没有返回 a SortedMap
,但迭代行为仍将被排序:
// from MapLike
override def mapValues[C](f: B => C): Map[A, C] = new DefaultMap[A, C] {
def iterator = for ((k, v) <- self.iterator) yield (k, f(v))
...
您的问题的解决方案与解决视图问题的解决方案相同:使用.map{ case (k,v) => (k,f(v)) }
,正如您在问题中提到的那样。
如果你真的想要那种方便的方法,你可以做我所做的,写你自己的,更好的,版本mapValues
:
class EnrichedWithMapVals[T, U, Repr <: GenTraversable[(T, U)]](self: GenTraversableLike[(T, U), Repr]) {
/**
* In a collection of pairs, map a function over the second item of each
* pair. Ensures that the map is computed at call-time, and not returned
* as a view as 'Map.mapValues' would do.
*
* @param f function to map over the second item of each pair
* @return a collection of pairs
*/
def mapVals[R, That](f: U => R)(implicit bf: CanBuildFrom[Repr, (T, R), That]) = {
val b = bf(self.asInstanceOf[Repr])
b.sizeHint(self.size)
for ((k, v) <- self) b += k -> f(v)
b.result
}
}
implicit def enrichWithMapVals[T, U, Repr <: GenTraversable[(T, U)]](self: GenTraversableLike[(T, U), Repr]): EnrichedWithMapVals[T, U, Repr] =
new EnrichedWithMapVals(self)
现在,当您调用mapVals
a时,SortedMap
您会得到一个 non-view SortedMap
:
scala> val m3 = m1.mapVals(_ + 1)
m3: SortedMap[String,Int] = Map(aardvark -> 2, cow -> 6, dog -> 10)
它实际上适用于任何对集合,而不仅仅是Map
实现:
scala> List(('a,1),('b,2),('c,3)).mapVals(_+1)
res8: List[(Symbol, Int)] = List(('a,2), ('b,3), ('c,4))