【问题标题】:unexpected side effect of map and for loop in scalascala中map和for循环的意外副作用
【发布时间】:2012-04-18 16:05:53
【问题描述】:

也许是一个很容易回答的问题,但我没有从 API 中找到令人满意的答案。我不是想写出漂亮的代码,而是想更多地了解某些事情是如何工作的:

我已经创建了一个初始 HashMap。从任意列表中,我想使用 map 来创建 HashMaps 列表。我在 .map 函数中向我的 HashMap 添加了一个(键,值)对。因此,在此列表中的任何后续索引中,存储的 HashMap 的大小都应增加一(因此 list(i).size == list(i+1).size - 1 对于每个 i)。

但是通过截断以下代码,我得到了一个完全相等的 Map 列表(即完整的 HashMap)。不过,如果我打印出不断增长的初始 HashMap,一切似乎都是正确的。

scala> import scala.collection.mutable.HashMap

val m = new HashMap[Int, Int]

List(1,2,3,4) map {e =>
    println(m)
    val newM = m += e -> (e+2)
    newM
}
Map()
Map(1 -> 3)
Map(1 -> 3, 2 -> 4)
Map(3 -> 5, 1 -> 3, 2 -> 4)

import scala.collection.mutable.HashMap
m: scala.collection.mutable.HashMap[Int,Int] = Map(3 -> 5, 4 -> 6, 1 -> 3, 2 -> 4)
res7: List[scala.collection.mutable.HashMap[Int,Int]] = List(Map(3 -> 5, 4 -> 6, 1 -> 3, 2 -> 4), Map(3 -> 5, 4 -> 6, 1 -> 3, 2 -> 4), Map(3 -> 5, 4 -> 6, 1 -> 3, 2 -> 4),     Map(3 -> 5, 4 -> 6, 1 -> 3, 2 -> 4))

我的预期是这样的

列表(地图(1 -> 2), 地图(1 -> 2, 2 -> 4), 地图(1 -> 2, 2 -> 4, 3 -> 5), 地图(1 -> 2 , 2 -> 4, 3 -> 5, 4 -> 6))

使用生成式 for 循环的等效(我假设是)版本会产生完全相同的结果:

scala> for {
    i <- 1 to 4
    val newM = m += i -> (i+2)
} yield newM
 res10: scala.collection.immutable.IndexedSeq[scala.collection.mutable.HashMap[Int,Int]] = Vector(Map(3 -> 5, 4 -> 6, 1 -> 3, 2 -> 4), Map(3 -> 5, 4 -> 6, 1 -> 3, 2 -> 4), Map(3 -> 5, 4 -> 6, 1 -> 3, 2 -> 4), Map(3 -> 5, 4 -> 6, 1 -> 3, 2 -> 4))

scala> for (i <- 1 to 4) yield {
    val newM = m += i -> (i+2)
    println(m)
    newM
} 
Map(1 -> 3)
Map(1 -> 3, 2 -> 4)
Map(3 -> 5, 1 -> 3, 2 -> 4)
Map(3 -> 5, 4 -> 6, 1 -> 3, 2 -> 4)
res11: scala.collection.immutable.IndexedSeq[scala.collection.mutable.HashMap[Int,Int]] = Vector(Map(3 -> 5, 4 -> 6, 1 -> 3, 2 -> 4), Map(3 -> 5, 4 -> 6, 1 -> 3, 2 -> 4), Map(3 -> 5, 4 -> 6, 1 -> 3, 2 -> 4), Map(3 -> 5, 4 -> 6, 1 -> 3, 2 -> 4))

我想我错过了一个基本步骤,但我只是看不出是哪个。感谢您提供任何进一步的帮助!

随后的问题:显然 (key, value) 对的打印顺序总是相同的(尽管不是按照它们是元素的顺序,但无论如何我都不希望 Map 出现这种情况,这是自然的)。当存储对时,后台会发生什么情况似乎总是如此?

【问题讨论】:

    标签: scala for-loop map hashmap side-effects


    【解决方案1】:

    m 是一个可变的 HashMap。

    += 方法将一个新元素添加到同一张地图,并返回该地图。

    您已将地图命名为newM,但这是一条红鲱鱼!这只是同一张地图m。所以他们都只是m。

    如果你真的想要一份副本,你可以拨打clonem(例如val newM = (m += e -&gt; (e+2)).clone)。

    或者,如果你想构建一个不可变的地图,你可以使用scan方法:

    List(1,2,3,4).scanLeft(collection.immutable.HashMap[Int,Int]()){ (m,e) => m + (e -> (e+2)) }
    

    获取一个项目(在本例中为空地图)并逐步构建它,完成后将整个历史记录返回给您:

    res7: List[scala.collection.immutable.HashMap[Int,Int]]
      = List(Map(), Map(1 -> 3), Map(1 -> 3, 2 -> 4),
             Map(1 -> 3, 2 -> 4, 3 -> 5), Map(1 -> 3, 2 -> 4, 3 -> 5, 4 -> 6))
    

    更好的是,你可以

    List(1,2,3,4).map(e => e -> (e+2)).toMap
    

    无需担心构建过程即可获得最终地图。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-06-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多