遍历ArrayList易犯错误 场景:
将ArrayList中符合条件的记录删掉,第一时间写出的程序如下:
foreach (string aStr in aList)
{
if (aStr.Equals(textBox1.Text))
{
aList.Remove(aStr);
}
}
似乎没有错误,编译也通过的,但运行时如果真的遇到符合条件的数据,则会抛出错误:
简单的解决办法是如何呢?这时用Clone方法最好不过了,用如下代码:
ArrayList bList = (ArrayList)aList.Clone();
foreach (string aStr in bList)
{
if (aStr.Equals(textBox1.Text))
{
aList.Remove(aStr);
}
}
似乎集合类型都会有这样的问题的。
posted on 2004-09-08 13:53 风前絮~~ 阅读(1047) 评论(25) 编辑 收藏
评论
# re: 遍历ArrayList易犯错误
用for就可以避免这样的问题,而且for的执行效率还高过foreach
2004-09-08 14:10 | 什么都不知道
# re: 遍历ArrayList易犯错误
啊,你.....你居然...居然敢用foreach来reomove,这是臭名昭著的Collection问题.....
2004-09-08 14:24 | 寒枫天伤
# re: 遍历ArrayList易犯错误
我一般是用
foreach(object item in al.ToArray())
..
或者是 in new ArrayList(somecollection).
2004-09-08 14:55 | Lostinet
# re: 遍历ArrayList易犯错误
好象是有一个浅表拷贝的方法。
最好是用哪个,不要用Clone
2004-09-08 15:11 | hyifeng
# re: 遍历ArrayList易犯错误
用for语句反向遍历即可
2004-09-08 16:07 | feilng
# re: 遍历ArrayList易犯错误
我也犯过这样的错误,呵呵
2004-09-08 16:10 | cure
# re: 遍历ArrayList易犯错误
To 什么都不知道:
for的方法也可以啊,代码如下?
for (int i=0;i<aList.Count;i++)
if (aList[i].Equals(textBox1.Text))
aList.RemoveAt(i);
To Lostinet :
你的方法也不错啊
To hyifeng:
对于ArrayList,Clone已经是一个浅表副本了。你说的是MemberwiseClone吗?
看来这种处理已经有三种方法了,foreach里面用Clone,for循环,ToArray,不知道那种比较好呢?
如何可以获得比较的数据?
2004-09-08 16:12 | 风前絮~~
# re: 遍历ArrayList易犯错误
To feilng:
反向遍历?有什么好处啊?大致如何实现呢?
2004-09-08 16:18 | 风前絮~~
# re: 遍历ArrayList易犯错误
我也有过,for应该是最安全的,而且简单易懂
Clone和ToArray至少增加了处理量
2004-09-08 16:23 | dali
# re: 遍历ArrayList易犯错误
To 风前絮~~ :
对于for的代码,应该增加一个条件分支,调用了RemoveAt方法的话,index不能够增加。也就是说循环递增语句不应该写在for的括号里面。
2004-09-08 17:56 | FantasySoft
# re: 遍历ArrayList易犯错误
受不了你们了,竟然。。。。竟然这种低级的错误都犯!!!!一个还不要紧,竟然是一群!!!晕~
删除的代码如下:
for (int i=aList.Count; --i >=0;)
if (aList[i].Equals(textBox1.Text))
aList.RemoveAt(i);
2004-09-08 18:54 | 老翅
# re: 遍历ArrayList易犯错误
呵呵~
2004-09-08 19:20 | hBifTs
# re: 遍历ArrayList易犯错误
To 老翅:
哈哈~~ 话也不能这么说了,编程那里会没有错误呢?要不也不会有BUG了。
感谢你贴的代码,我试过了,没有问题,可以说是for的一种方法。
To FantasySoft :
我那个for的代码也是通过了的,可以达到效果,请指明我代码错误,感谢!
2004-09-08 19:58 | 风前絮~~
# re: 遍历ArrayList易犯错误
To 风前絮~~ :代码本身语法没有错,只是逻辑上有问题而已。因为RemoveAt方法会改变Count方法的返回值,就造成了不是每个元素都被遍历到。这也是feilng和老翅提出反向遍历的原因。
请看以下代码:
using System.Collections;
using System;
class Test
{
public static void Main()
{
ArrayList test = new ArrayList();
for(int i=0; i < 3; i++)
test.Add('test');
test.Add('testAgain');
test.Add('testAgain');
for(int i=0; i < test.Count; i++)
{
if (test[i].Equals('test'))
test.RemoveAt(i);
}
for(int i = 0; i < test.Count;i++)
Console.WriteLine(test[i]);
}
}
test这个ArrayList里面的'test'是不是应该都被remove掉呢?事实上如果这样写,还是会剩下一个的。
2004-09-09 02:22 | FantasySoft
# re: 遍历ArrayList易犯错误
To FantasySoft:
十分感谢! 原来的代码因为每个不相同的,因此没有发现这个问题。
仔细想了下,确实如你所说: Count变了,使得index=0的被漏掉了。
看来for的方法只有反向遍历了。
有空用ILDASM看了下三种不同方法生成的代码,比较对应函数,发现用的堆栈最大值一样,代码长度不同,for的最短,ToArray()居中,Clone最多。
正向for的方法要加个语句就可以了,但看起来比反向臃肿了:
for (int i=0;i<aList.Count;i++)
if (aList[i].Equals(textBox1.Text))
{
aList.RemoveAt(i);
i--;
}
2004-09-09 09:18 | 风前絮~~
# re: 遍历ArrayList易犯错误
To 风前絮~~ :可以这样写
for (int i=0;i<aList.Count;)
if (aList[i].Equals(textBox1.Text))
{
aList.RemoveAt(i);
}
else
i++;
这样写是不是就更清晰了呢?for的递增语句不一定要写到for的括号里面的。
2004-09-09 10:24 | Fantasysoft
# re: 遍历ArrayList易犯错误
对呀,也是个方法! ^_^
2004-09-09 10:36 | 风前絮~~
# re: 遍历ArrayList易犯错误
恕我直言,看到楼主你的“仔细想了下,确实如你所说:Count变了,使得index=0的被漏掉了。”这句话,感觉你并没有真正理解为什么会产生楼顶的问题
被遗漏的并不是原list的0号item,而是1号item
为什么呢?因为当你remove第i个item时,第[i + 1, count)域中所有items的索引值皆减一(Array是连续的,要满足只要有k ∈ [0, count)则Array[k]必存在),那么当你在循环下一轮用i + 1为索引访问的即是原先list中索引为i + 2的那个item,由此可知,上例中0号被删除时,原1号变为新0号,原2号变为新1号。。。下一轮访问的1号就是先前的2号,而原始的1号则永远没有被访问到
明白了这个自然可以得出正确的迭代方法,正序时需注意索引和可能变化的终止条件,倒序时则简单些
2004-09-09 14:44 | 问题男
# re: 遍历ArrayList易犯错误
To 问题男:
Oh~~ Sorry,估计是我的表达有问题。
index=0是Count变了后的,它原来的index确实=1,正如你所说的。
感谢指正!
2004-09-09 15:16 | 风前絮~~
# re: 遍历ArrayList易犯错误
re: 遍历ArrayList易犯错误
用for语句反向遍历即可
2004-09-08 16:07 | feilng
完全同意,我觉得这是最好的办法了
p.s.这种事情,没有出过错的人第一次都会写错的,哈哈
2004-09-09 16:56 | myrat
# re: 遍历ArrayList易犯错误
看看这段代码,会有什么结果?
int[] myarray = {1,2,3};
foreach(int num in myarray){
num++;
}
2004-09-09 17:21 | juqiang
# re: 遍历ArrayList易犯错误
To juqing:
有趣啊
应该是不行的
--------------------------------------------------------------------------------
MSDN中的说明:
foreach 语句为数组或对象集合中的每个元素重复一个嵌入语句组。foreach 语句用于循环访问集合以获取所需信息,但不应用于更改集合内容以避免产生不可预知的副作用。
--------------------------------------------------------------------------------
num++ 等价于 num = num + 1,修改内容了。
编译报错:
...(201): 无法分配到“num”,因为它是只读的
2004-09-09 17:33 | 风前絮~~
# 遍历ArrayList易犯错误[TrackBack]
Ping Back来自:blog.csdn.net
windsails引用了该文章,地址:http://blog.csdn.net/windsails/archive/2004/09/10/100331.aspx
2004-09-10 13:21 | windsails
# re: 遍历ArrayList易犯错误
为什么要反向?
int i =0;
while( i<aList.Count)
{
if (aList[i].Equals(textBox1.Text))
{
aList.RemoveAt(i)
continue;
}
i++;
}
2004-10-20 21:29 | 大力水手
# re: 遍历ArrayList易犯错误
反向简单且效率高啊,aList.Count只在初始化时用一次,其他情况要每次都用啊。虽说aList.Count也是变量,但从机器码的角度来说,间接地址引用还是要花比寄存器引用多很多的时间的。
2004-12-09 12:45 | 无名