Java|Java 遍历集合删除元素()

  • 问题及分析
  • 正确删除集合元素
问题及分析 注:在浏览阿里巴巴Java开发手册时,自己测试Java遍历集合并删除元素时发现有些巧合以及总结
【Java|Java 遍历集合删除元素()】先写开发手册里一个例子,大家猜一下以下代码的输出
List 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 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 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 { 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 = https://www.it610.com/article/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 list = new ArrayList<>(); list.add("w"); list.add("li"); list.add("z"); System.out.println("list original size is " + list.size()); Iterator 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()); }

    推荐阅读