【问题标题】:Java - Collection.remove() behaves differently in different conditionsJava - Collection.remove() 在不同的条件下表现不同
【发布时间】:2013-01-08 15:20:40
【问题描述】:

这是我上一个问题的后续:

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

代码抛出异常!

【问题讨论】:

  • java.util.ConcurrentModificationException
  • 但它如何取决于“if”条件?
  • 所以它会抛出 noob1 或 noob3?
  • 是否有任何一般规则可以确定它?或者它是未定义的!
  • 一般规则是什么?方法不对,改用Iterator,调试器看看哪里抛出异常。

标签: java collections iterator


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      实际上,您不应该在“随意”迭代期间删除集合的元素。当您必须在某个循环中修改您的收藏时,您必须使用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】:

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

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

        【讨论】:

          【解决方案5】:

          这是因为您试图从 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);
               }
           }
          

          应该可以。希望

          【讨论】:

            【解决方案6】:

            好吧,您的第一个案例不会抛出异常,因为当您删除索引 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这是假的

            【讨论】:

              猜你喜欢
              • 2018-05-10
              • 1970-01-01
              • 1970-01-01
              • 2020-06-02
              • 1970-01-01
              • 2021-07-19
              • 2016-06-11
              • 1970-01-01
              • 2022-09-22
              相关资源
              最近更新 更多