mapValues和Map中的变换之间的区别

elm*_*elm 24 dictionary scala scala-collections

在斯卡拉Map(见API)是什么样的语义表现之间的差异,mapValues以及transform

例如,对于任何给定的地图

val m = Map( "a" -> 2, "b" -> 3 )
Run Code Online (Sandbox Code Playgroud)

m.mapValues(_ * 5)
m.transform( (k,v) => v * 5 )
Run Code Online (Sandbox Code Playgroud)

提供相同的结果.

Kig*_*gyo 43

假设我们有一个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)
Run Code Online (Sandbox Code Playgroud)

这样做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)
}
Run Code Online (Sandbox Code Playgroud)

(取自https://github.com/scala/scala/blob/v2.11.2/src/library/scala/collection/MapLike.scala#L244)

因此,性能方面取决于更有效的方法.如果f价格昂贵并且您只访问生成的地图的一些元素,则mapValues可能会更好,因为f仅按需应用.否则我会坚持maptransform.

transform也可以表达map.假设m: Map[A,B]f: (A,B) => C,然后

m.transform(f) 相当于 m.map{case (a, b) => (a, f(a, b))}


som*_*ytt 9

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
Run Code Online (Sandbox Code Playgroud)

可变变换变异到位:

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)
Run Code Online (Sandbox Code Playgroud)

所以可变变换不会改变地图的类型:

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)
Run Code Online (Sandbox Code Playgroud)

......在构建新地图时可能会发生这种情况.


cod*_*to7 7

这里有几个未提及的差异:

  • mapValues创建一个不可序列化的Map,没有任何迹象表明它只是一个视图(类型是Map[_, _],但只是尝试通过线路发送一个).

  • 由于mapValues只是一个视图,每个实例都包含真实Map- 这可能是另一个结果mapValues.想象一下,你有一个具有某种状态的演员,并且状态的每个变异都将新状态设置为mapValues前一个状态...最后你有深度嵌套的地图,其中包含演员的每个先前状态的副本(和是的,这些都来自经验).