filterKeys会导致堆栈溢出吗?

时间:2012-01-31 14:18:03

标签: scala collections stack-overflow

据我了解,filterKeys的方法MapLike会在原始地图上创建包装。所以,如果我执行下面的代码m将是一个包含10K包装器和原始地图的链。

var m = Map(1 -> "one", 2 -> "two")
for(1 <- 0 until 10000) {m = m.filterKeys(_%2 == 0)}

现在我认为调用m.contains会导致堆栈溢出但不会发生。你能解释一下这个案子的情况吗?

3 个答案:

答案 0 :(得分:2)

我无法重现堆栈溢出,但这是发生的事情:

override def filterKeys(p: A => Boolean): Map[A, B] = new DefaultMap[A, B] {
  override def foreach[C](f: ((A, B)) => C): Unit = for (kv <- self) if (p(kv._1)) f(kv)
  def iterator = self.iterator.filter(kv => p(kv._1))
  override def contains(key: A) = self.contains(key) && p(key)
  def get(key: A) = if (!p(key)) None else self.get(key)
}

请注意,没有复制值:新类只是将规则添加到四个方法中,这四个方法将改变它们的工作方式以反映您添加的过滤器。由于您反复应用filterKeys(10000次),这意味着您有10000个类,每个类指向前一个类,第一个指向原始地图。

因此,在最终类中调用上述方法之一(直接或间接)将调用10000个嵌套方法,如果堆栈足够小,这肯定会产生堆栈溢出。

答案 1 :(得分:2)

我能够像这样使用Stack Overflow(Scala 2.9.1)

scala> var m = Map(1 -> "one", 2 -> "two")
scala> for (_ <- 0 until 1000000) { m = m.filterKeys(_ % 2 == 0) }
scala> m.contains(1)
huge stack trace

当然,您可以通过强制filterKeys在每一步实际执行其工作来避免堆栈跟踪:

scala> var m = Map(1 -> "one", 2 -> "two")
scala> for (_ <- 0 until 1000000) { m = Map() ++ m.filterKeys(_ % 2 == 0) }
scala> m.contains(1)
res1: Boolean = false

答案 2 :(得分:1)

如果我逐字复制,你的循环只执行一次。因此,你只创建一个包装器,所以打算成为10000个包装器的链只是一个1的链。它可能是一个错字,但循环,

for(1 <- 0 until 10000) {m = m.filterKeys(_%2 == 0)}

应该是

for(i <- 0 until 10000) {m = m.filterKeys(_%2 == 0)}

除此之外,丹尼尔是对的; fitlerKeys确实生产了一个基本上是包装器的东西。我花了超过10k次迭代,但我确实设法创建了一个StackOverflowError。