在Scala中Map
(请参阅API)mapValues
和transform
之间的语义和效果有何不同?
对于任何给定的地图,例如
val m = Map( "a" -> 2, "b" -> 3 )
两个
m.mapValues(_ * 5)
m.transform( (k,v) => v * 5 )
提供相同的结果。
答案 0 :(得分:38)
假设我们有Map[A,B]
。澄清一下:我总是提到一个不可变的Map
。
mapValues
采用函数B => C
,其中C
是值的新类型。
transform
采用函数(A, B) => C
,其中C
也是值的类型。
因此两者都会产生Map[A,C]
。
但是使用transform
功能,您可以通过键值来影响新值的结果。
例如:
val m = Map( "a" -> 2, "b" -> 3 )
m.transform((key, value) => key + value) //Map[String, String](a -> a2, b -> b3)
使用mapValues
执行此操作会非常困难。
下一个区别是transform
是严格的,而mapValues
只会给你一个视图,它不会存储更新的元素。它看起来像这样:
protected class MappedValues[C](f: B => C) extends AbstractMap[A, C] with DefaultMap[A, C] {
override def foreach[D](g: ((A, C)) => D): Unit = for ((k, v) <- self) g((k, f(v)))
def iterator = for ((k, v) <- self.iterator) yield (k, f(v))
override def size = self.size
override def contains(key: A) = self.contains(key)
def get(key: A) = self.get(key).map(f)
}
(摘自https://github.com/scala/scala/blob/v2.11.2/src/library/scala/collection/MapLike.scala#L244)
因此,性能方面取决于更有效的方法。如果f
价格昂贵且您只访问生成的地图的一些元素,mapValues
可能会更好,因为f
仅在需要时应用。否则我会坚持map
或transform
。
transform
也可以用map
表示。假设m: Map[A,B]
和f: (A,B) => C
,然后
m.transform(f)
相当于m.map{case (a, b) => (a, f(a, b))}
答案 1 :(得分:8)
collection.Map
未提供transform
:它为可变和不可变地图提供了不同的签名。
$ scala
Welcome to Scala version 2.11.2 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_11).
Type in expressions to have them evaluated.
Type :help for more information.
scala> val im = Map('a -> 1, 'b -> 2, 'c -> 3)
im: scala.collection.immutable.Map[Symbol,Int] = Map('a -> 1, 'b -> 2, 'c -> 3)
scala> im.mapValues(_ * 7) eq im
res0: Boolean = false
scala> im.transform { case (k,v) => v*7 } eq im
res2: Boolean = false
scala> val mm = collection.mutable.Map('a -> 1, 'b -> 2, 'c -> 3)
mm: scala.collection.mutable.Map[Symbol,Int] = Map('b -> 2, 'a -> 1, 'c -> 3)
scala> mm.mapValues(_ * 7) eq mm
res3: Boolean = false
scala> mm.transform { case (k,v) => v*7 } eq mm
res5: Boolean = true
Mutable transform变异:
scala> mm.transform { case (k,v) => v*7 }
res6: mm.type = Map('b -> 98, 'a -> 49, 'c -> 147)
scala> mm.transform { case (k,v) => v*7 }
res7: mm.type = Map('b -> 686, 'a -> 343, 'c -> 1029)
因此,可变变换不会改变地图的类型:
scala> im mapValues (_ => "hi")
res12: scala.collection.immutable.Map[Symbol,String] = Map('a -> hi, 'b -> hi, 'c -> hi)
scala> mm mapValues (_ => "hi")
res13: scala.collection.Map[Symbol,String] = Map('b -> hi, 'a -> hi, 'c -> hi)
scala> mm.transform { case (k,v) => "hi" }
<console>:9: error: type mismatch;
found : String("hi")
required: Int
mm.transform { case (k,v) => "hi" }
^
scala> im.transform { case (k,v) => "hi" }
res15: scala.collection.immutable.Map[Symbol,String] = Map('a -> hi, 'b -> hi, 'c -> hi)
......构建新地图时可能会发生这种情况。
答案 2 :(得分:7)
以下是一些未提及的差异:
mapValues
创建一个不可序列化的地图,没有任何迹象表明它只是一个视图(类型为Map[_, _]
,但只是尝试通过网络发送一个)。
由于mapValues
只是一个视图,因此每个实例都包含真实的Map
- ,这可能是mapValues
的另一个结果。想象一下,你有一个具有某种状态的演员,并且状态的每个变异都将新状态设置为前一个状态的mapValues
...最后你有深度嵌套的地图,其中包含每个先前状态的副本演员(并且,是的,这些都来自经验)。