1

这是对我之前的问题的跟进:

集合 - Iterator.remove() 与 Collection.remove()

下面两段代码,显然只有一行不同,但一个抛出异常,另一个不抛出异常。你能解释一下区别吗?

 List<String> list = new ArrayList<String>
(Arrays.asList("noob1","noob2","noob3"));


System.out.println(list);

for (String str : list) {
    if (str.equals("noob2")) {
        list.remove(str);
    }
}

运行良好,但如果我将条件更改为

if (!str.equals("noob2"))

代码抛出异常!

4

6 回答 6

5

在这种情况下会发生什么是您正在删除第二个列表元素。

List<String> list = new ArrayList<String>
        (Arrays.asList("noob1", "noob2", "noob3", "noob4"));

System.out.println(list);

for (Iterator<String> iterator = list.iterator(); iterator.hasNext(); ) {
    String str = iterator.next();
    if (str.equals("noob3")) {
        System.out.println("Checking "+str);
        list.remove(str);
    }
}
System.out.println(list);

印刷

[noob1, noob2, noob3, noob4]
Checking noob1
Checking noob2
Checking noob3
[noob1, noob2, noob4]

通过删除倒数第二个元素,您将大小减少到您已迭代的元素数量。

// from ArrayList.Itr
    public boolean hasNext() {
        return cursor != size;
    }

这会导致循环在执行并发修改检查之前提前退出next()。如果您删除任何其他元素next(),则会调用并获得 CME。

顺便说一句,也绕过检查的东西是

for (Iterator<String> iterator = list.iterator(); iterator.hasNext(); ) {
    String str = iterator.next();
    System.out.println("Checking "+str);
    if (str.equals("noob2")) {
        list.remove("noob1");
        list.remove("noob3");
    }
}

只要集合的大小与它所达到的索引相同,就不会执行检查。

于 2013-01-08T15:24:48.413 回答
1

for 循环只是列表迭代器扫描的简化语法。如果列表在其下被修改,迭代器可能会抛出异常,但不能保证。由于 hasNext,迭代器通常会提前处理一个元素,从而使第一种情况不太可能受到列表修改的影响。当“noob2”被移除时,迭代器已经知道“noob3”。

于 2013-01-08T15:23:51.763 回答
1

我想这个例外是因为你试图改变你正在循环的集合......而不是因为if条件。

我建议您创建一个仅包含验证条件的项目的新列表。将它们添加到新列表中,避免更改原始集合。

于 2013-01-08T15:23:53.597 回答
1

实际上,您永远不应该在“随意”迭代期间删除集合的元素。当您必须在某个循环中修改您的集合时,您必须使用iterator它们来进行这些操作。

    public class Test {
        public static void main(String... args) {
            List<String> list = new ArrayList<String>(Arrays.asList("noob1", "noob2", "noob3"));

            System.out.println(list);

            for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
                String str = iterator.next();
                if (!str.equals("noob2")) {
                    iterator.remove();
                }
            }
            System.out.println(list);
        }
    }
于 2013-01-08T15:29:31.140 回答
1

这是因为您正试图从Collection您当前正在迭代的对象中删除。做一个小的改动,你可以做你想做的事:

 String[] strValues = {"noob1","noob2","noob3"}; // <<< Array
 List<String> list = new ArrayList<String>(Arrays.asList(strValues));

 System.out.println(list);

 for (String str : strValues) { // << List is duplicate of array so can iterate through array
     if (!str.equals("noob2")) {
         list.remove(str);
     }
 }

那应该行得通。希望

于 2013-01-08T15:32:36.413 回答
0

好吧,您的第一个案例不会抛出异常,因为当您删除索引 1 处的元素时,迭代器在索引 2 处为 Iterator.hasNext() 返回 false。

Iterator<String> itr = list.iterator();
    while(itr.hasNext()){
        String s= itr.next();
        if(s.equals("noob2")){
            list.remove(s); // size of the list is 2 here 
            System.out.println(itr.hasNext());// this returns false as it doesn't have anything at index 2 now.(on 2nd iteration )
        }
    }

您可以使用简单的 for 循环清楚地测试它:

for (int i=0; i<list.size(); i++) {
        if (list.get(i).equals("noob2")) {
            System.out.println(list.get(i));
            System.out.println(list.size());
            list.remove(list.get(i));
            System.out.println(list.size());
        }
    }

输出:

[noob1, noob2, noob3]
noob2
3
2

注意删除元素后列表的大小,增加后失败。2<2这是错误的

于 2013-01-08T15:33:32.740 回答