如何在删除某些元素时迭代 Scala ArrayBuffer?
How do I iterate over a Scala ArrayBuffer while some elements are removed?
我有一些来自游戏的 Scala 代码,旨在在 mutable.ArrayBuffer
、gameobjects
中的每个 GameObject
实例上调用 onInteract(gameData: GameData, player: Player)
方法。但是,有时这些 GameObject
会使用代码 gameobjects -= this
从 ArrayBuffer
中删除自己。由于这改变了 ArrayBuffer
,Scala 抛出一个 NullPointerException
。代码可以被认为类似于以下内容:
for (gameobject <- gameobjects) {
if (/* some condition */) gameobjects -= gameobject
}
并会在删除对象时抛出异常。
我该如何补救?我认为 ArrayBuffer
或至少 for
循环在这里不合适。
虽然我无法在 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 的方法是使用过滤器(或 filterNot):
val a = ArrayBuffer(1, 2, 2, 3)
a.filter { _ != 2 } // == ArrayBuffer(1, 3)
使用 -=
从 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 集合完美地复制这种行为有点困难,所以为了真正安全,您可能需要像上面那样手写操作。
我有一些来自游戏的 Scala 代码,旨在在 mutable.ArrayBuffer
、gameobjects
中的每个 GameObject
实例上调用 onInteract(gameData: GameData, player: Player)
方法。但是,有时这些 GameObject
会使用代码 gameobjects -= this
从 ArrayBuffer
中删除自己。由于这改变了 ArrayBuffer
,Scala 抛出一个 NullPointerException
。代码可以被认为类似于以下内容:
for (gameobject <- gameobjects) {
if (/* some condition */) gameobjects -= gameobject
}
并会在删除对象时抛出异常。
我该如何补救?我认为 ArrayBuffer
或至少 for
循环在这里不合适。
虽然我无法在 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 的方法是使用过滤器(或 filterNot):
val a = ArrayBuffer(1, 2, 2, 3)
a.filter { _ != 2 } // == ArrayBuffer(1, 3)
使用 -=
从 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 集合完美地复制这种行为有点困难,所以为了真正安全,您可能需要像上面那样手写操作。