使用普通for循环时删除元素

验证代码:

  1. List<String> list = new ArrayList<>();
  2. list.add("1");
  3. list.add("2");
  4. list.add("3");
  5. for (int i = 0; i < list.size(); i++) {
  6. System.out.println(list.get(i));
  7. list.remove(i);
  8. }
  9. // 只会打印出1,3,会漏掉2

此时删除元素,会导致元素前移,当i为1时,实际上访问的时原数组下标为2的数据,而原数组中下标为1的,此时已经前移到下标0处,所以不能在使用普通for循环时删除元素

使用增强for循环时删除元素

原始代码:

  1. List<String> list = new ArrayList<>();
  2. list.add("1");
  3. list.add("2");
  4. list.add("3");
  5. for (String s : list) {
  6. list.remove(s);
  7. }

增强for循环在编译后会被优化为迭代器

反编译代码:

  1. List<String> list = new ArrayList();
  2. list.add("1");
  3. list.add("2");
  4. list.add("3");
  5. Iterator var4 = list.iterator();
  6. while(var4.hasNext()) {
  7. String s = (String)var4.next();
  8. list.remove(s);
  9. }

而迭代器在每次使用next方法获取下一个元素时,会检查内部的modCount变量与expectedModCount变量是否一致,而List的remove方法在移除元素时,不会更新迭代器中的expectedModCount变量,只更新了全局的modCount,导致在next时实际值与期望值不符。

ArrayList的remove源码:

ArrayList遍历删除问题 - 图1

迭代器的next源码(红线标注的地方则为检查modCount值的方法):

ArrayList遍历删除问题 - 图2]

使用迭代器时删除元素

验证代码:

  1. List<String> list = new ArrayList();
  2. list.add("1");
  3. list.add("2");
  4. list.add("3");
  5. Iterator iterator = list.iterator();
  6. while(iterator.hasNext()) {
  7. String next = (String)iterator.next();
  8. iterator.remove();
  9. }

迭代器删除时,必须先调用next方法,然后在调用remove方法

原因: 在remove时使用lastRet下标变量去删除数组元素的,而初始化时默认的时-1且每次删除后都会置为-1,如果不调用next方法将当前元素的下标赋予lastRet,则remove时,会直接抛出非法状态异常

迭代器remove方法:

ArrayList遍历删除问题 - 图3

红线1的位置调用ArrayList的remove方法移除元素,此时修改了modCount,

红线2的位置将modCount值同步到了expectedModCount变量

并在1,2之间的代码更新了下一次下标的位置

同步后在下一次next时,检查modCount就不会抛出ConcurrentModificationExecption异常