解读ArrayList集合中,for循环遍历和迭代器遍历的不同

/**
 * 解读ArrayList集合中,for循环遍历和迭代器遍历的不同
 *
 * @author MoCha
 * @date 2020/4/3
 */
public class Demo {
    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>(Arrays.asList(1, 23, 4, 5));

        // An optimized version of AbstractList.ListItr
        // 从源码可以看出java.util.ArrayList.ListItr是进行过优化的版本
        // 内部继承了java.util.ArrayList.Itr,同时实现了java.util.ListIterator接口
        // java.util.ArrayList.ListItr提供更多方法给我们操作集合,比如hasPrevious, add方法
        // 所以如果想要在遍历的时候,为ArrayList添加元素,可以使用listIterator()
        ListIterator<Integer> integerListIterator = arrayList.listIterator();
        while (integerListIterator.hasNext()) {
            // integerListIterator.add(1);
            // 但凡使用remove操作,都需要先调用next方法,因为remove操作方法是ListIterator继承自java.util.ArrayList.Itr
            // 如果不先调用next方法,那么lastRet始终为-1,在调用remove方法时,会抛出IllegalStateException();
            integerListIterator.next();
            integerListIterator.remove();
            if (arrayList.size() < 2) {
                break;
            }
        }

        System.out.println(arrayList);
        // for循环遍历,实际上用到了next方法,否则,for循环左边的值怎么取的呢
        // 而next过程中会判断modCount是否有改变,所以不能在for循环中调用ArrayList的add, remove等方法
        // 这些方法,都会使得集合的modCount值发生变化,从而快速失败
        // 由debug模式下,调试以下代码可以知道,当第一次进入该循环时,是可以对ArrayList进行添加操作的,
        // 不过需要注意的是此时ArrayList的modCount已经发生变化,所以第二次,也就是调用next()方法的时候,检查checkForComodification时出现异常
        // 同理,remove也会

        // 如果说为什么要用iterator迭代器进行遍历,而不是用for循环来操作
        // 最主要的原因是,你无法在for循环里面,对集合的元素进行改动,否则会报异常
        // 而使用迭代器的方式,你可以调用ArrayList的listIterator(),而这个java.util.ArrayList.ListItr提供了add, remove方法
        for (Integer integer : arrayList) {
            // 从源代码可以看出,java.util.ArrayList.add(E)操作是会Increments modCount
            // ensureCapacityInternal(size + 1);  // Increments modCount!!
            arrayList.add(1);
        }
        System.out.println(arrayList);
    }
}

运行结果

[5]
Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
	at java.util.ArrayList$Itr.next(ArrayList.java:859)
	at top.yangzefeng.movie.Demo.main(Demo.java:43)

总结:

  • for循环中,无法对ArrayList进行改动,否则会出现ConcurrentModificationException

  • 如果仅仅只是对ArrayList进行读取遍历,使用forEach会更加简洁

  • 如果想要在迭代时对ArrayList进行改动,推荐使用listIterator()方法,该方法返回的java.util.ArrayList.ListItr,继承了ArrayList内部的Itr,实现了ListIterator接口,提供了如hasPrevious, add方法,更加方便我们在遍历时操作ArrayList

  • 需要注意的是,在迭代时,不能上来直接就调用remove方法,因为此时lastRet始终为-1,在调用remove方法时,会抛出IllegalStateException();

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();
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章