微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Scala:为什么SortedMap的mapValues返回Map而不是SortedMap?

我是 Scala新手.
我在我的代码中使用了 SortedMap,我想使用mapValues来创建一个新的地图,并对这些值进行一些转换.

而不是返回一个新的SortedMap,mapValues函数返回一个新的Map,然后我必须转换为SortedMap.

例如

val my_map = SortedMap(1 -> "one",0 -> "zero",2 -> "two")
val new_map = my_map.mapValues(name => name.toupperCase)
// returns scala.collection.immutable.Map[Int,java.lang.String] = Map(0 -> ZERO,1 -> ONE,2 -> TWO)
val sorted_new_map = SortedMap(new_map.toArray:_ *)

这看起来效率不高 – 最后的转换可能会再次分配密钥,或者至少验证它们是否被排序.

我可以使用在键和值上操作的法线贴图功能,并且故意不改变我的转换功能中的键.这看起来效率不高,因为Map的实现可能假设转换可能会改变键的顺序(就像在这样的情况:my_map.map(tup =>(-tup._1,tup._2))),所以它他们也可能“重新排序”他们.

有人熟悉Map和SortedMap的内部实现,可以告诉我我的假设是否正确?编译器能否自动识别钥匙是否未重新排序?为什么mapValues不应该返回SortedMap是否有内部原因?有没有更好的方式来转换地图的值而不会失去键的顺序?

谢谢

解决方法

您偶然发现了Scala的地图实施的棘手功能.你缺少的catch是mapValues实际上并没有返回一个新的Map:它返回一个Map的视图.换句话说,它包装你的原始地图,只要你访问一个值,它将计算.toupperCase,然后返回值.

这种行为的优点是Scala不会计算未被访问的值的函数,并且不会花费时间将所有数据复制到新的Map中.缺点是每次访问该值时都会重新计算该函数.因此,如果您访问相同的值多次,您可能会最终进行额外的计算.

那么为什么SortedMap不返回SortedMap?因为它实际上是返回一个Map-wrapper.底层的Map,然后是一个被包装的Map,仍然是一个SortedMap,所以如果你要迭代,它仍然是排序顺序.你和我知道,但类型检查没有.看起来好像他们可以写这样一种方式,它仍然保持SortedMap特性,但是没有.

您可以在代码中看到它没有返回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,Repr]): EnrichedWithMapVals[T,Repr] =
  new EnrichedWithMapVals(self)

现在,当您在SortedMap上调用mapVals时,您将返回一个非查看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,3),4))

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐