2016-06-03 12 views
1

Ich habe etwas Scala-Code von einem Spiel, das die onInteract(gameData: GameData, player: Player)-Methode für jede Instanz eines GameObject in einem mutable.ArrayBuffer, gameobjects aufrufen soll. Manchmal entfernen sich diese GameObject s jedoch selbst aus dem ArrayBuffer, indem sie den Code gameobjects -= this verwenden. Da dies die ArrayBuffer ändert, wirft Scala eine NullPointerException. Der Code könnte ähnlich sein zu folgenden betrachtet:Wie kann ich über einen Scala ArrayBuffer iterieren, während einige Elemente entfernt werden?

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

und würde eine Ausnahme ausgelöst, wenn ein Objekt entfernt wird.

Wie kann ich das beheben? Ich würde mir vorstellen, ein ArrayBuffer, oder zumindest die for Schleife, ist hier ungeeignet.

Antwort

1

Obwohl ich nicht die Ausnahme in scala 2.11.8 reproduzieren können Sie sehen, dass das Ergebnis nicht korrekt ist:

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) 

Ich vermute, dies geschieht, da jetzt das Array weniger Elemente hat, dass, wenn Sie Iterieren gestartet .

Ein besserer, scala Ansatz ist Filter zu verwenden (oder filterNot):

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

Dies ist in ähnlicher Weise ein Problem in Python: http://Stackoverflow.com/q/1207406/1240268 (nicht während der Iteration ändern, haben Sie eine schlechte Zeit!) –

+0

Dies ändert das Verhalten durch Erstellen eines neuen 'ArrayBuffer' statt das vorhandene zu modifizieren. – vossad01

1

-= Mit etwas von einem ArrayBuffer ist wirklich langsam entfernen - Sie Element für Element zu suchen haben, es zu finden , dann mische alles bis zum Ende des Puffers herum. Das solltest du sowieso nicht tun.

Wenn Sie gleichzeitig iterieren und löschen möchten, sollten Sie eine Datenstruktur verwenden, die dies unterstützt. java.util.concurrent.ConcurrentHashMap ist oft eine gute Wahl, wenn Sie Ihre Objekte nach Identität suchen wollen und keine Duplikate haben.

Zum Beispiel:

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 

Es ist ein wenig schwer, dieses Verhalten mit den Scala Sammlungen perfekt zu replizieren, so wirklich sicher zu sein, das Sie wie oben die Dinge tun, Langschrift mögen.

+0

Haben Sie aus Gründen der Vollständigkeit eine Empfehlung, wenn es sich um Duplikate handelt (und Sie nur eine entfernen möchten)? – vossad01

+0

@ vossad01 - Nichts schönes. Sie können immer eine andere veränderbare Auflistung als Werttyp hinzufügen und den gesamten Eintrag entfernen, wenn der Wert leer ist. (Und oder behalten Sie einfach eine Zählung, wenn es keinen anderen Wert gibt.) –

Verwandte Themen