Java 遍历集合删除元素?

版权声明:开源交流,可以不进行特殊介绍,但请尊重个人劳动成果,互相交流哦! https://blog.csdn.net/wanglizheng825034277/article/details/81391118
  • 问题及分析
  • 正确删除集合元素

问题及分析

注:在浏览阿里巴巴Java开发手册时,自己测试Java遍历集合并删除元素时发现有些巧合以及总结

先写开发手册里一个例子,大家猜一下以下代码的输出

List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");

System.out.println("list original size is " + list.size());

for (String item : list) {
    if ("1".equals(item)) {
        list.remove(item);
    }
}

System.out.println("list size is " + list.size());
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");

System.out.println("list original size is " + list.size());

for (String item : list) {
    if ("1".equals(item)) {
        list.remove(item);
    }
}

System.out.println("list size is " + list.size());

上面两个例子的输出结果是什么?意外吗?
1,2可以成功;1, 2, 3删除抛出异常

  • 第一个:
    list original size is 2
    list size is 1
  • 第二个:
    list original size is 3
    java.util.ConcurrentModificationException

我们可以看一下此处代码的反编译代码:

List<String> list = new ArrayList();
list.add("1");
list.add("2");
// list.add("3");
System.out.println("list original size is " + list.size());
Iterator var2 = list.iterator();

while(var2.hasNext()) {
    String item = (String)var2.next();
        if ("1".equals(item)) {
            list.remove(item);
        }
}

System.out.println("list size is " + list.size());

通过查看反编译代码,发现我们使用的foreach遍历仍然是迭代器Iterator遍历,而ArrayList中Iterator源码:

 private class Itr implements Iterator<E> {
     int cursor;       // index of next element to return
     int lastRet = -1; // index of last element returned; -1 if no such
     int expectedModCount = modCount;

     Itr() {}

     public boolean hasNext() {
         return cursor != size;   // 游标
     }

     @SuppressWarnings("unchecked")
     public E next() {
         checkForComodification();   // 检查是否并发修改
         int i = cursor;
         if (i >= size)
             throw new NoSuchElementException();
         Object[] elementData = ArrayList.this.elementData;
         if (i >= elementData.length)
             throw new ConcurrentModificationException();
         cursor = i + 1;  // 游标后移
         return (E) elementData[lastRet = i];
     }

     public void remove() {
         if (lastRet < 0)
             throw new IllegalStateException();
         checkForComodification();

         try {
             ArrayList.this.remove(lastRet);
             cursor = lastRet;
             lastRet = -1;
             expectedModCount = modCount;   // 修改预期值(迭代器删除的重要参考)
         } catch (IndexOutOfBoundsException ex) {
             throw new ConcurrentModificationException();
         }
     }
     ......
  }

modCount是集合添加,删除等改变集合结构的次数(改变集合大小),expectedModCount是预期的变化次数;
分析一下:
[“1”, “2”] : 当”1”被遍历删除后,游标cursor的值从0变为1,集合长度也变为1,这是hasNext返回false,比较表示没有下一个元素,结束遍历;
[“1”, “2”, “3”] : 如上,当”1”遍历删除后,游标cursor从0变为1,集合长度变为2,hasNext返回true,执行remove时,checkForComodification()方法验证是否同时修改,此方法表modCount != expectedModCount,modCount是3,expectedModCount也是3,而在删除后modCount变为4,而hasNext()方法不返回false,next()方法调用时就会抛出异常;
注:由其游标变化规律可以看出,如果hasNext提前结束,不执行后面的next取数据,就可以删除集合元素,故可以删除集合中倒数第二个元素而不抛出异常(实践也如此)


正确删除集合元素

  • 迭代器方式
 public void positiveForEachTest() {
      List<String> list = new ArrayList<>();
      list.add("w");
      list.add("li");
      list.add("z");

      System.out.println("list original size is " + list.size());

      Iterator<String> iterator = list.iterator();
      while (iterator.hasNext()) {
          String item = iterator.next();
          if ("li".equals(item)) {
              iterator.remove();
          }
      }

      System.out.println("after list remove elem `li`, it's size is " + list.size());
  }

猜你喜欢

转载自blog.csdn.net/wanglizheng825034277/article/details/81391118