迭代时从NSMutableArray中删除的最佳方法?

时间:2020-03-06 14:30:50  来源:igfitidea点击:

在可可中,如果我要遍历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秒。