Meilleure façon de supprimer à partir de NSMutableArray lors de l'itération?
Dans le Cacao, si je veux faire une boucle par un NSMutableArray et supprimer plusieurs objets qui correspondent à certains critères, quelle est la meilleure façon de le faire sans avoir à redémarrer la boucle à chaque fois que j'ai supprimer un objet?
Merci,
Edit: Juste pour préciser - je cherchais le meilleur moyen, par exemple, quelque chose de plus élégant que manuellement la mise à jour de l'index, je suis à la. Par exemple en C++ je peux le faire;
iterator it = someList.begin();
while (it != someList.end())
{
if (shouldRemove(it))
it = someList.erase(it);
}
- Boucle à partir de l'arrière vers l'avant.
- Pas de réponse au "POURQUOI"
- L'un de mes tous les temps favoris et les plus sous-estimés de la solution dans la programmation en général 😀
Vous devez vous connecter pour publier un commentaire.
Pour plus de clarté, je tiens à faire une première boucle où je rassemble les éléments à supprimer. Puis-je les supprimer. Voici un exemple de l'aide Objective-C 2.0 syntaxe:
Alors il n'est pas question de savoir si les indices sont mis à jour correctement, ou autres peu de tenue de la comptabilité de détails.
Modifiées afin d'ajouter:
Il a été noté dans d'autres réponses que la formulation inverse devrait être plus rapide. c'est à dire Si vous parcourir le tableau et la composition d'un tableau d'objets à garder, au lieu de les objets à jeter. C'est peut-être vrai (bien que ce sujet de la mémoire et du coût de traitement d'allouer un nouveau tableau, et le rejet de l'ancien?) mais même si c'est plus rapide, il peut ne pas être aussi important que ce serait pour une implémentation naïve, parce que NSArrays ne se comportent pas comme des tableaux "normaux". Ils parlent l'entretien, mais ils marchent sur un autre pied. Voir une bonne analyse ici:
La formulation inverse peut être plus rapide, mais je n'ai jamais eu besoin de se préoccuper de savoir si c'est parce que l'énoncé ci-dessus a toujours été assez rapide pour mes besoins.
Pour moi le message est à utiliser quelle que soit la formulation est plus claire pour vous. Optimiser uniquement si nécessaire. Personnellement, je trouve la formule la plus claire, c'est pourquoi je l'utilise. Mais si la formulation inverse est plus clair pour vous, d'aller pour elle.
Une variation. Ainsi, vous obtenez la lisibilité et de la bonne performance:
removeObjectsAtIndexes
est la pire méthode pour supprimer les objets, êtes-vous d'accord avec ça? Je demande cela parce que votre réponse est trop vieux maintenant. Néanmoins, il sera bon de choisir le meilleur?enumerateObjectsUsingBlock:
vous obtenir l'indice d'échelon gratuit.C'est très simple problème. Vous venez d'effectuer une itération à l'envers:
C'est un schéma très commun.
Certains des autres réponses auraient de mauvaises performances sur des tableaux de très grande taille, parce que les méthodes comme
removeObject:
etremoveObjectsInArray:
impliquent de faire une recherche linéaire du récepteur, qui est une perte de temps parce que vous le savez déjà où se trouve l'objet. Aussi, tout appel àremoveObjectAtIndex:
devrez copier les valeurs de l'index à la fin du tableau par une fente à la fois.Plus efficace serait la suivante:
Parce que nous avons fixé la capacité de
itemsToKeep
, nous ne perdez pas de temps à copier les valeurs lors d'un redimensionnement. Nous n'avons pas modifier le tableau en place, de sorte que nous sommes libres d'utiliser Rapide Énumération. À l'aide desetArray:
pour remplacer le contenu dearray
avecitemsToKeep
sera efficace. En fonction de votre code, vous pouvez même remplacer la dernière ligne avec:Donc il n'y a même pas besoin de copier les valeurs, permuter uniquement un pointeur.
Vous pouvez utiliser NSpredicate pour supprimer des éléments de votre mutable tableau. Ceci nécessite pas de boucles.
Par exemple, si vous avez un NSMutableArray de noms, vous pouvez créer un prédicat comme celui-ci:
La ligne suivante va vous laisser avec un tableau qui contient uniquement les noms commençant par b.
Si vous avez de la difficulté à créer les prédicats dont vous avez besoin, utilisez ce apple developer lien.
J'ai fait un test de performance à l'aide de 4 méthodes différentes. Chaque test itérer sur tous les éléments de 100.000 élément de tableau, et enlevé toutes les 5ème élément. Les résultats ne varient pas beaucoup avec/sans optimisation. Ceci a été fait sur un iPad 4:
(1)
removeObjectAtIndex:
-- 271 ms(2)
removeObjectsAtIndexes:
-- 1010 ms (parce que la construction de l'indice d'ensemble prend ~700 ms; sinon, c'est essentiellement le même que l'appel à la removeObjectAtIndex: pour chaque élément)(3)
removeObjects:
-- 326 ms(4) faire un tableau avec des objets, en passant le test -- 17 ms
Donc, création d'un nouveau tableau est de loin le plus rapide. Les autres méthodes sont toutes comparables, sauf que l'utilisation removeObjectsAtIndexes: ce sera pire avec plus d'éléments à supprimer, en raison du temps nécessaire à la construction de l'indice d'ensemble.
Soit utiliser la boucle de comptage vers le bas sur les indices:
ou de faire une copie avec les objets que vous souhaitez garder.
En particulier, ne pas utiliser de
for (id object in array)
boucle ouNSEnumerator
.Pour iOS 4+ ou OS X 10.6+, Apple a ajouté
passingTest
série d'Api dansNSMutableArray
, comme– indexesOfObjectsPassingTest:
. Une solution avec ces API serait:Aujourd'hui, vous pouvez utiliser inversé basé sur des blocs de l'énumération. Un exemple simple de code:
Résultat:
une autre option avec juste une ligne de code:
Dans un plus déclaratif, selon les critères de correspondance les éléments à supprimer, vous pouvez utiliser:
@Nathan doit être très efficace
Voici le facile et propre. Je tiens à dupliquer mon tableau à droite dans le rapide énumération appel:
De cette façon, vous énumérer une copie du tableau étant supprimé, les deux possédant les mêmes objets. Un NSArray détient pointeurs d'objet uniquement si cela est tout à fait bien de la mémoire/performance sage.
for (LineItem *item in self.lineItems.copy)
Ajouter les objets que vous souhaitez supprimer pour un second tableau, et, après la boucle, utilisez -removeObjectsInArray:.
cela devrait le faire:
espère que cela aide...
Pourquoi ne pas ajouter les objets à supprimer à l'autre NSMutableArray. Lorsque vous avez terminé l'itération, vous pouvez supprimer les objets que vous avez recueillies.
Comment sur la permutation des éléments que vous souhaitez supprimer à l'aide du n-ième élément, 'n-1 ième élément, et ainsi de suite?
Lorsque vous avez terminé, vous redimensionner le tableau précédent-taille - nombre de contrats de swaps de'
Si tous les objets de votre tableau sont uniques ou que vous souhaitez supprimer toutes les occurrences d'un objet trouvé, vous pouvez rapidement énumérer sur un tableau à copier et à utiliser [NSMutableArray removeObject:] pour supprimer l'objet de l'original.
+arrayWithArray
est en cours d'exécution?benzado la réponse ci-dessus est ce que vous devez faire pour preformace. Dans une de mes applications removeObjectsInArray a eu une durée de 1 minute, juste l'ajout d'un nouveau tableau a pris .023 secondes.
De définir une catégorie qui me permet de filtrer à l'aide d'un bloc, comme ceci:
qui peut ensuite être utilisé comme ceci:
Une meilleure mise en œuvre pourrait être l'utilisation de la catégorie de la méthode ci-dessous sur NSMutableArray.
Le prédicat bloc peut être mis en œuvre pour faire le traitement sur chaque objet du tableau. Si le prédicat renvoie true, l'objet est supprimé.
Un exemple pour une date de tableau pour supprimer toutes les dates qui se trouvent dans le passé:
De l'itération en arrière-ly a été mon préféré pendant des années , mais pendant longtemps je n'ai jamais rencontré le cas où l 'profonde' ( plus grand nombre) de l'objet a été retiré en premier. Momentanément avant de le pointeur se déplace vers le prochain indice il n'y a pas rien et il se bloque.
Benzado de la façon la plus proche de ce que je fais maintenant, mais je n'ai jamais réalisé qu'il y aurait de la pile remaniement après tous les supprimer.
sous Xcode 6 cela fonctionne