Wenn ich in Cocoa ein NSMutableArray durchlaufen und mehrere Objekte entfernen möchte, die bestimmten Kriterien entsprechen, wie kann ich dies am besten tun, ohne die Schleife jedes Mal neu zu starten, wenn ich ein Objekt entferne?
Vielen Dank,
Bearbeiten: Nur zur Verdeutlichung - Ich habe nach dem besten Weg gesucht, z. B. nach etwas Eleganterem, als den Index, an dem ich mich befinde, manuell zu aktualisieren. Zum Beispiel in C ++ kann ich tun;
iterator it = someList.begin();
while (it != someList.end())
{
if (shouldRemove(it))
it = someList.erase(it);
}
objective-c
cocoa
Andrew Grant
quelle
quelle
Antworten:
Aus Gründen der Klarheit möchte ich eine erste Schleife erstellen, in der ich die zu löschenden Elemente sammle. Dann lösche ich sie. Hier ist ein Beispiel mit der Objective-C 2.0-Syntax:
Dann gibt es keine Frage, ob die Indizes korrekt aktualisiert werden oder andere kleine Buchhaltungsdetails.
Bearbeitet, um hinzuzufügen:
In anderen Antworten wurde festgestellt, dass die inverse Formulierung schneller sein sollte. dh wenn Sie das Array durchlaufen und ein neues Array von Objekten erstellen, die beibehalten werden sollen, anstatt Objekte, die verworfen werden sollen. Das mag wahr sein (obwohl was ist mit den Speicher- und Verarbeitungskosten für das Zuweisen eines neuen Arrays und das Verwerfen des alten Arrays?), Aber selbst wenn es schneller ist, ist es möglicherweise nicht so wichtig wie für eine naive Implementierung, weil NSArrays Verhalten Sie sich nicht wie "normale" Arrays. Sie reden, aber sie gehen einen anderen Weg. Eine gute Analyse finden Sie hier:
Die inverse Formulierung mag schneller sein, aber ich musste mich nie darum kümmern, ob dies der Fall ist, da die obige Formulierung für meine Bedürfnisse immer schnell genug war.
Für mich ist die Botschaft zum Mitnehmen, die für Sie klarste Formulierung zu verwenden. Nur bei Bedarf optimieren. Ich persönlich finde die obige Formulierung am klarsten, weshalb ich sie verwende. Aber wenn Ihnen die umgekehrte Formulierung klarer ist, machen Sie es.
quelle
Noch eine Variation. So erhalten Sie Lesbarkeit und gute Leistung:
quelle
removeObjectsAtIndexes
die schlechteste Methode zum Entfernen der Objekte ist. Sind Sie damit einverstanden? Ich frage dies, weil Ihre Antwort jetzt zu alt ist. Trotzdem ist es gut, das Beste zu wählen?enumerateObjectsUsingBlock:
Sie erhalten das Indexinkrement kostenlos.Dies ist ein sehr einfaches Problem. Sie iterieren einfach rückwärts:
Dies ist ein sehr verbreitetes Muster.
quelle
Einige der anderen Antworten hätten eine schlechte Leistung bei sehr großen Arrays, da Methoden eine lineare Suche des Empfängers mögen
removeObject:
undremoveObjectsInArray:
beinhalten, was eine Verschwendung ist, da Sie bereits wissen, wo sich das Objekt befindet. Außerdem muss jeder Aufruf vonremoveObjectAtIndex:
Werte um jeweils einen Steckplatz vom Index bis zum Ende des Arrays kopieren.Effizienter wäre Folgendes:
Da wir die Kapazität von festlegen
itemsToKeep
, verschwenden wir keine Zeit damit, Werte während einer Größenänderung zu kopieren. Wir ändern das Array nicht an Ort und Stelle, daher können wir Fast Enumeration verwenden. MitsetArray:
auf den Inhalt ersetztarray
mititemsToKeep
wird effizient sein. Abhängig von Ihrem Code können Sie sogar die letzte Zeile durch Folgendes ersetzen:Sie müssen also nicht einmal Werte kopieren, sondern nur einen Zeiger austauschen.
quelle
Mit NSpredicate können Sie Elemente aus Ihrem veränderlichen Array entfernen. Dies erfordert kein for-Schleifen.
Wenn Sie beispielsweise ein NSMutableArray mit Namen haben, können Sie ein Prädikat wie dieses erstellen:
In der folgenden Zeile erhalten Sie ein Array, das nur Namen enthält, die mit b beginnen.
Wenn Sie Probleme beim Erstellen der benötigten Prädikate haben, verwenden Sie diesen Apple-Entwicklerlink .
quelle
Ich habe einen Leistungstest mit 4 verschiedenen Methoden durchgeführt. Jeder Test durchlief alle Elemente in einem Array mit 100.000 Elementen und entfernte jedes fünfte Element. Die Ergebnisse variierten mit / ohne Optimierung nicht sehr. Diese wurden auf einem iPad 4 durchgeführt:
(1)
removeObjectAtIndex:
- 271 ms(2)
removeObjectsAtIndexes:
- 1010 ms (da das Erstellen des Indexsatzes ~ 700 ms dauert; ansonsten entspricht dies im Grunde dem Aufruf von removeObjectAtIndex: für jedes Element)(3)
removeObjects:
- 326 ms(4) Erstellen Sie ein neues Array mit Objekten, die den Test bestehen - 17 ms
Das Erstellen eines neuen Arrays ist also bei weitem am schnellsten. Die anderen Methoden sind alle vergleichbar, mit der Ausnahme, dass die Verwendung von removeObjectsAtIndexes: aufgrund der zum Erstellen des Indexsatzes erforderlichen Zeit mit mehr zu entfernenden Elementen schlechter ist.
quelle
Verwenden Sie entweder eine Schleife, die über Indizes herunterzählt:
oder erstellen Sie eine Kopie mit den Objekten, die Sie behalten möchten.
Verwenden Sie insbesondere keine
for (id object in array)
Schleife oderNSEnumerator
.quelle
Für iOS 4+ oder OS X 10.6+ hat Apple eine
passingTest
Reihe von APIs hinzugefügtNSMutableArray
, z– indexesOfObjectsPassingTest:
. Eine Lösung mit einer solchen API wäre:quelle
Heutzutage können Sie die umgekehrte blockbasierte Aufzählung verwenden. Ein einfacher Beispielcode:
Ergebnis:
Eine weitere Option mit nur einer Codezeile:
quelle
Je nach den Kriterien, die den zu entfernenden Elementen entsprechen, können Sie deklarativer Folgendes verwenden:
@ Nathan sollte sehr effizient sein
quelle
Hier ist der einfache und saubere Weg. Ich möchte mein Array direkt im Aufruf der schnellen Aufzählung duplizieren:
Auf diese Weise führen Sie eine Kopie des Arrays auf, aus dem gelöscht wird, wobei beide dieselben Objekte enthalten. Ein NSArray enthält nur Objektzeiger, sodass dies in Bezug auf Speicher und Leistung völlig in Ordnung ist.
quelle
for (LineItem *item in self.lineItems.copy)
Fügen Sie die Objekte, die Sie entfernen möchten, zu einem zweiten Array hinzu und verwenden Sie nach der Schleife -removeObjectsInArray:.
quelle
das sollte es tun:
hoffe das hilft...
quelle
Warum fügen Sie die zu entfernenden Objekte nicht einem anderen NSMutableArray hinzu? Wenn Sie mit dem Iterieren fertig sind, können Sie die gesammelten Objekte entfernen.
quelle
Wie wäre es, wenn Sie die Elemente, die Sie löschen möchten, gegen das 'n'-te Element, das' n-1'-te Element usw. austauschen?
Wenn Sie fertig sind, ändern Sie die Größe des Arrays auf "vorherige Größe - Anzahl der Swaps".
quelle
Wenn alle Objekte in Ihrem Array eindeutig sind oder Sie alle Vorkommen eines Objekts entfernen möchten, wenn es gefunden wurde, können Sie eine Array-Kopie schnell auflisten und das Objekt mit [NSMutableArray removeObject:] aus dem Original entfernen.
quelle
+arrayWithArray
der Ausführung aktualisiert wird?Benzados Antwort oben ist das, was Sie für die Vorformung tun sollten. In einer meiner Anwendungen dauerte removeObjectsInArray eine Laufzeit von 1 Minute, das Hinzufügen zu einem neuen Array dauerte 0,023 Sekunden.
quelle
Ich definiere eine Kategorie, mit der ich mithilfe eines Blocks filtern kann:
die dann so verwendet werden kann:
quelle
Eine schönere Implementierung könnte darin bestehen, die unten stehende Kategoriemethode für NSMutableArray zu verwenden.
Der Prädikatblock kann implementiert werden, um die Verarbeitung für jedes Objekt im Array durchzuführen. Wenn das Prädikat true zurückgibt, wird das Objekt entfernt.
Ein Beispiel für ein Datumsarray zum Entfernen aller in der Vergangenheit liegenden Daten:
quelle
Rückwärts iterieren war jahrelang mein Favorit, aber lange Zeit bin ich nie auf den Fall gestoßen, dass das 'tiefste' Objekt (höchste Anzahl) zuerst entfernt wurde. Kurz bevor der Zeiger zum nächsten Index übergeht, gibt es nichts und es stürzt ab.
Benzados Weg kommt dem, was ich jetzt mache, am nächsten, aber ich hätte nie gedacht, dass es nach jedem Entfernen zu einer Stapelumbildung kommen würde.
unter Xcode 6 funktioniert das
quelle