簡體   English   中英

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

[英]Scala: Why does SortedMap's mapValues returns a Map and not a SortedMap?

我是Scala的新手。 我在我的代碼中使用了SortedMap ,我想使用mapValues創建一個新的地圖,並對值進行一些轉換。

mapValues函數返回一個新的Map ,而不是返回一個新的SortedMap ,然后我必須將其轉換為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函數,它既可以對鍵和值進行操作,也可以故意不改變轉換函數中的鍵。 這也看起來效率低,因為Map的實現可能假設轉換可能會改變鍵的順序(例如: my_map.map(tup => (-tup._1, tup._2) ) - 所以很可能“重新排序”他們。

有人熟悉MapSortedMap的內部實現,可以告訴我我的假設是否正確嗎? 編譯器是否可以自動識別密鑰尚未重新排序? 是否有內部原因導致mapValues不應返回SortedMap 有沒有更好的方法來轉換地圖的值而不會失去鍵的順序?

謝謝

您偶然發現了Scala Map實現的一個棘手功能。 您缺少美中不足的是, mapValues實際上並不返回一個新的Map :它返回一個view一的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, U, Repr <: GenTraversable[(T, U)]](self: GenTraversableLike[(T, U), Repr]): EnrichedWithMapVals[T, U, 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,2), ('b,3), ('c,4))

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM