迭代时从NSMutableArray中删除的最佳方法?
在可可中,如果我要遍历NSMutableArray并删除多个符合特定条件的对象,那么在每次删除对象时都无需重新启动循环的最佳方法是什么?
谢谢,
编辑:只是为了澄清我正在寻找最好的方法,例如比手动更新我所在的索引更优雅的方法。例如,在C ++中,我可以做到;
iterator it = someList.begin(); while (it != someList.end()) { if (shouldRemove(it)) it = someList.erase(it); }
解决方案
将要删除的对象添加到第二个数组,然后在循环之后使用-removeObjectsInArray:。
为什么不将要删除的对象添加到另一个NSMutableArray。完成迭代后,可以删除已收集的对象。
使用循环递减索引:
for (NSInteger i = array.count - 1; i >= 0; --i) {
或者使用我们要保留的对象制作副本。
特别是,请勿使用" for(数组中的id对象)"循环或者" NSEnumerator"。
这应该做到这一点:
NSMutableArray* myArray = ....; int i; for(i=0; i<[myArray count]; i++) { id element = [myArray objectAtIndex:i]; if(element == ...) { [myArray removeObjectAtIndex:i]; i--; } }
希望这可以帮助...
以更具声明性的方式,根据与要删除的项目匹配的条件,可以使用:
[theArray filterUsingPredicate:aPredicate]
@Nathan应该非常有效率
如何将要删除的元素与第n个元素,第n-1个元素等等交换?
完成后,将数组的大小调整为"以前的交换大小"
为了清楚起见,我希望创建一个初始循环,在其中收集要删除的项目。然后我删除它们。这是使用Objective-C 2.0语法的示例:
NSMutableArray *discardedItems = [NSMutableArray array]; for (SomeObjectClass *item in originalArrayOfItems) { if ([item shouldBeDiscarded]) [discardedItems addObject:item]; } [originalArrayOfItems removeObjectsInArray:discardedItems];
毫无疑问,索引是否被正确更新,或者其他小的簿记细节。
编辑添加:
在其他答案中已经指出,逆公式应该更快。也就是说,如果我们遍历数组并组成一个新的对象数组,以保留而不是丢弃的对象。这可能是正确的(尽管分配一个新数组并丢弃旧数组会占用多少内存和处理成本?),但是即使更快,它也可能不像天真的实现那么重要,因为NSArrays行为不像"普通"数组。他们说话,但走的路却不同。在这里查看良好的分析:
逆公式可能会更快,但是我从来不需要关心它是否是反斜,因为上面的公式一直足够快以满足我的需求。
对我来说,带回家的信息是使用我们最清楚的任何表达方式。仅在必要时进行优化。我个人认为上述配方最清晰,这就是为什么要使用它。但是,如果我们对逆公式更清楚,那就继续吧。
如果数组中的所有对象都是唯一的,或者要在找到对象时删除所有出现的对象,则可以快速枚举数组副本并使用[NSMutableArray removeObject:]从原始对象中删除对象。
NSMutableArray *myArray; NSArray *myArrayCopy = [NSArray arrayWithArray:myArray]; for (NSObject *anObject in myArrayCopy) { if (shouldRemove(anObject)) { [myArray removeObject:anObject]; } }
其他一些答案在非常大的数组上的性能会很差,因为诸如removeObject:
和removeObjectsInArray:
之类的方法涉及对接收器进行线性搜索,这很浪费,因为我们已经知道对象在哪里。同样,对removeObjectAtIndex:
的任何调用都必须一次将索引中的值复制到数组的末尾,最多一次复制一个插槽。
以下是更有效的方法:
NSMutableArray *array = ... NSMutableArray *itemsToKeep = [NSMutableArray arrayWithCapacity:[array count]]; for (id object in array) { if (! shouldRemove(object)) { [itemsToKeep addObject:object]; } } [array setArray:itemsToKeep];
因为我们设置了" itemsToKeep"的容量,所以我们不会在调整大小时浪费任何时间来复制值。我们没有在适当的地方修改数组,因此我们可以自由使用快速枚举。使用setArray:
将array
的内容替换为itemsToKeep
会很有效。根据代码,我们甚至可以将最后一行替换为:
[array release]; array = [itemsToKeep retain];
因此,甚至不需要复制值,只需交换一个指针。
我们可以使用NSpredicate从可变数组中删除项目。这不需要for循环。
例如,如果我们具有名称的NSMutableArray,则可以创建这样的谓词:
NSPredicate *caseInsensitiveBNames = [NSPredicate predicateWithFormat:@"SELF beginswith[c] 'b'"];
下一行将为我们提供一个仅包含以b开头的名称的数组。
[namesArray filterUsingPredicate:caseInsensitiveBNames];
如果在创建所需谓词时遇到困难,请使用此Apple开发人员链接。
另一种变化。这样我们将获得可读性和良好的性能:
NSMutableIndexSet *discardedItems = [NSMutableIndexSet indexSet]; SomeObjectClass *item; NSUInteger index = 0; for (item in originalArrayOfItems) { if ([item shouldBeDiscarded]) [discardedItems addIndex:index]; index++; } [originalArrayOfItems removeObjectsAtIndexes:discardedItems];
上面的benzado的答案是我们应该做的事。在我的一个应用程序中,removeObjectsInArray的运行时间为1分钟,仅将其添加到新数组中的时间为.023秒。