【问题标题】:Difference between mapValues and transform in MapMap中mapValues和transform的区别
【发布时间】:2014-10-27 10:50:03
【问题描述】:

在 Scala 中 Map(参见 APImapValuestransform 之间的语义和性能有何不同?

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

val m = Map( "a" -> 2, "b" -> 3 )

两者

m.mapValues(_ * 5)
m.transform( (k,v) => v * 5 )

提供相同的结果。

【问题讨论】:

    标签: scala dictionary scala-collections


    【解决方案1】:

    假设我们有一个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 仅按需应用。否则我会坚持使用maptransform

    transform也可以用map表示。假设m: Map[A,B]f: (A,B) =&gt; C,那么

    m.transform(f) 等价于m.map{case (a, b) =&gt; (a, f(a, b))}

    【讨论】:

    • 绝妙的答案!谢谢!
    • If f is expensive and you only access a few elements of the resulting map, mapValues might be better, since f is only applied on demand 请注意,如果f 的开销很大并且元素被大量访问,那么使用mapValues 的性能非常糟糕。
    【解决方案2】:

    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
    

    可变变换就地变异:

    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)
    

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

    【讨论】:

      【解决方案3】:

      以下是一些未提及的差异:

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

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

      【讨论】:

        猜你喜欢
        • 2015-11-17
        • 2021-03-11
        • 2016-08-10
        • 1970-01-01
        • 1970-01-01
        • 2011-03-18
        • 2014-06-30
        • 2021-02-18
        • 1970-01-01
        相关资源
        最近更新 更多