Java - Collection.remove()在不同条件下的行为有所不同

时间:2013-01-08 15:20:41

标签: java collections iterator

这是对我上一个问题的跟进:

Collection - Iterator.remove() vs 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"))

代码抛出异常!

6 个答案:

答案 0 :(得分:4)

在这种情况下,您要移除第二个列表元素。

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。

BTW还绕过支票的东西是

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");
    }
}

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

答案 1 :(得分:1)

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

答案 2 :(得分:1)

我认为异常是因为您正在尝试更改正在循环的集合...而不是因为if条件。

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

答案 3 :(得分: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);
        }
    }

答案 4 :(得分: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);
     }
 }

那应该有用。希望

答案 5 :(得分: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&lt; 2 这是假的