Java 遍历集合删除元素?

  • 问题及分析
  • 正确删除集合元素

问题及分析

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

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

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

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());
  }
  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值