【问题标题】:How do I iterate over a Scala ArrayBuffer while some elements are removed?如何在删除某些元素时迭代 Scala ArrayBuffer?
【发布时间】:2016-06-03 03:53:40
【问题描述】:

我有一些来自游戏的 Scala 代码,旨在在 mutable.ArrayBuffergameobjects 中的每个 GameObject 实例上调用 onInteract(gameData: GameData, player: Player) 方法。但是,有时这些GameObjects 会使用代码gameobjects -= thisArrayBuffer 中删除自己。由于这改变了ArrayBuffer,Scala 抛出了NullPointerException。可以认为代码类似于以下内容:

for (gameobject <- gameobjects) {
    if (/* some condition */) gameobjects -= gameobject
}

并且每当删除对象时都会引发异常。

我该如何解决这个问题?我想ArrayBuffer 或至少for 循环在这里不合适。

【问题讨论】:

    标签: scala for-loop arraylist arraybuffer


    【解决方案1】:

    虽然我无法在 scala 2.11.8 中重现异常,但您可以看到结果不正确:

    import scala.collection.mutable.ArrayBuffer
    val a = ArrayBuffer(1, 2, 2, 3)
    
    for (x <- a) {
       if (x == 2) a-=x
    }
    
    // There's still a 2!
    a: ArrayBuffer[Int] = ArrayBuffer(1, 2, 3)
    

    我怀疑发生这种情况是因为数组现在的元素比开始迭代时少。

    一种更好、更 scala 的方法是使用 filter(或 filterNot):

    val a = ArrayBuffer(1, 2, 2, 3)
    a.filter { _ != 2 } // == ArrayBuffer(1, 3)
    

    【讨论】:

    • 这同样是python中的一个问题:stackoverflow.com/q/1207406/1240268(迭代时不要修改,你会很糟糕!)
    • 这会通过创建一个新的ArrayBuffer 而不是修改现有的来改变行为。
    【解决方案2】:

    使用-=ArrayBuffer 中删除某些内容确实很慢——您必须逐个元素搜索才能找到它,然后将所有内容随机移动到缓冲区的末尾。无论如何,你可能不应该这样做。

    如果你想同时迭代和删除,你应该使用支持这个的数据结构。如果您想按身份查找对象并且不会有重复项,java.util.concurrent.ConcurrentHashMap 通常是一个不错的选择。

    例如:

    val chm = new java.util.concurrent.ConcurrentHashMap[String, Int]
    chm put ("fish", 1); chm put ("dish", 2); chm put ("wish", 3)
    val e = chm.keys
    e.hasMoreElements // true
    e.nextElement     // "wish"
    e.hasMoreElements // true
    chm remove "fish"
    chm remove "dish" // empty now!!
    e.nextElement     // "dish"--was going to be the next key
    e.hasMoreElements // false--now it realizes chm is empty
    chm get "dish"    // null, because it doesn't exist
    

    用 Scala 集合完美地复制这种行为有点困难,所以为了真正安全起见,您可能希望像上面那样直接执行操作。

    【讨论】:

    • 为了完整起见,当可能存在重复(并且您只想删除一个)时,您是否有建议?
    • @vossad01 - 没什么好看的。您始终可以添加另一个可变集合作为值类型,并在值为空时删除整个条目。 (或者只是保持计数,如果没有任何关联的值。)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-18
    • 1970-01-01
    • 1970-01-01
    • 2014-01-02
    • 2014-01-04
    相关资源
    最近更新 更多