遍历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 | 无名 

你可能感兴趣的:(编程,list,textbox)