很多时候遍历数组的同时需要删除数组里面符合条件的元素。那么哪种删除方法正确呢?错误的方法错在哪里?我觉得有必要分析一下。
方法一:for 循环,索引器i < [numberArray count] for循环遍历,没有用到枚举遍历的。因此程序是不会崩溃的。
NSMutableArray *numberArray = [[NSMutableArray alloc] init];
for (int i = 1; i < 6; i++) {
NSNumber *nsnumber = [NSNumber numberWithInt:i];
[numberArray addObject:nsnumber];
}
for (int i = 0; i < [numberArray count]; i++) {
NSNumber *number = [numberArray objectAtIndex:i];
if ([number intValue] == 3) {
[numberArray removeObject:number];
NSLog(@"删除的数为%@",number);
}
NSLog(@"%@",[numberArray objectAtIndex:i]);
}
NSLog(@"%@",numberArray);
输出结果为:1 、2、{删除的数为3、4}、5。 {1,2,4,5}
虽然删除了 数组中元素值为3的元素 ,输出了5个元素,但是循环次数其实变为了4次。因为在删除3的for循环中i值为2,删除i后,数组元素前移,本来下标为3的4元素,变为下标为2的4元素。if语句结束后执行NSLOG语句输出下标为2的4元素。 i++ 值变为3,输出 5。
关于测试代码规范的重要性。
上面代码不规范因为有NSLOG小细节需要注意,没有注意的化会认为循环次数没有改变,因此上面的代码不是合格的测试代码。
下面的代码清晰的展示了删除后数组下标的变化和循环次数。
for (int i = 0; i < [numberArray count]; i++) {
NSNumber *number = [numberArray objectAtIndex:i];
if ([number intValue] == 3) {
[numberArray removeObject:number];
NSLog(@"删除数时i的值为%d",i);
NSLog(@"删除的数为%@",number);
NSLog(@"删除后数组的长度为%ld",[numberArray count]);
NSLog(@"/**********************/");
} else
{
NSLog(@"当前i的值为%d",i);
NSLog(@"当前数组的长度为%ld",[numberArray count]);
NSLog(@"当前遍历的数为%@",[numberArray objectAtIndex:i]);
NSLog(@"/**********************/");
}
}
输出结果:0 ,1;1,2;2,删除3;3,5。 可以看到 删除3 后,4前移下标变为2,5前移下标变为3。一共循环输出了4次,出现了4元素没有被输出情况。
若数组改变后仍要输出下标为4的元素就回报错,报错如下:
*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 4 beyond bounds [0 .. 3]'
意思是说下标为4的元素,已经越界了。
综上所述,因此在for循环的时候,可变数组numberArray长度已经发生改变,且被删除元素后面的元素会先前移动。这类似于链表的存储结构。
// 待续。