• 【搬砖系列】如何在遍历List时安全删除集合元素


     1 public void testIterRemove() {
     2         List l1 = new ArrayList(Arrays.asList(1, 2, 3, 4, 5));
     3         Iterator<Integer> iterator = l1.iterator();
     4         System.out.println("before=" + l1);
     5         while (iterator.hasNext()) {
     6             iterator.next();
     7             iterator.remove();
     8         }
     9         System.out.println("after=" + l1);
    10 }
    11 /*
    12 before=[1, 2, 3, 4, 5]
    13 after=[]
    14 */
    遍历时移除元素的正确示例

    代码中关键的部分在于:

    iterator.next();

    iterator.remove();

    二者缺一不可。当然调用next()之前要调用hasNext()确保没有越界。

    简单分析两个方法的代码即可得出答案:

    首先看remove()方法,为什么hasNext() + remove()不可以呢:

     1         public void remove() {
     2             if (lastRet < 0)
     3                 throw new IllegalStateException();
     4             checkForComodification();
     5 
     6             try {
     7                 ArrayList.this.remove(lastRet);
     8                 cursor = lastRet;
     9                 lastRet = -1;
    10                 expectedModCount = modCount;
    11             } catch (IndexOutOfBoundsException ex) {
    12                 throw new ConcurrentModificationException();
    13             }
    14         }
    ArrayList#Itr.remove()

    由于代码用属性lastRet(即lastReturn index)控制异常,所以查看ArrayList#Itr的实例初始化语句,lastRet初值是-1,如果单独调用remove()会报错。

    再看一下next()方法,就会找出答案:

     1         public E next() {
     2             checkForComodification();
     3             int i = cursor;
     4             if (i >= size)
     5                 throw new NoSuchElementException();
     6             Object[] elementData = ArrayList.this.elementData;
     7             if (i >= elementData.length)
     8                 throw new ConcurrentModificationException();
     9             cursor = i + 1;
    10             return (E) elementData[lastRet = i];
    11         }
    ArrayList#Itr.next()

    在方法中首先将cursor记录到变量 i,返回前将 i 的知赋给 lastRet ,完成lastRet的更新。方法返回值不是本次要考虑的范畴。

    再回过头看remove()方法,有了lastRet后,就可以重置cursor。在方法中调用了ArrayList#remove(),这个方法会重新调整数组元素的位置,如果删掉的不是最后一个位置的元素(numMoved == 0 表示删掉的正好是最后一位元素),将会把index后面的元素往前移动一个,最后将elementData[--size] = null 为移除旧元素,帮助GC。

    总结:

    需要先调用next()更新lastRet的值,才能在remove()中通过第一关对lastRet的检查。因此在遍历List时需要联合使用 :

    while( it.hasNext() ) 
    { 
        it.next(); 
        it.remove();
    } 

    同理,其他集合删除应该也是这个步骤,没来得及验证,欢迎评论区小锤换大锤:)。

  • 相关阅读:
    Oracle 12C 在 Oracle Linux 6.5 64Bit 安装手册
    OWI之db file sequential read
    OWI之db file scattered read
    Linux监控脚本
    Java中的空值判断
    Java中的java.math.BigInteger
    Java中用正则表达式找出数字
    Java中的StringBuffer
    JAVA String.format 方法使用介绍
    Java中对整数格式化
  • 原文地址:https://www.cnblogs.com/christmad/p/9893272.html
Copyright © 2020-2023  润新知