第二家公司第一个功能的反思及遇到的集合遍历的坑

近期比较繁琐的一个功能,用到了不少之前根本没注意到的一些坑,近期项目进人QA测试阶段这边会慢慢的把对应的坑(可能对于基础好的童鞋这些不属于坑的范畴)以及解决的思路会统一整理。

此外在这次功能的实现过程中发现虽然LZ已经具备一年多的开发经验但是依然停留在简单的增删改查。当LZ有意识的去进行代码优化的对应操作确显得力不从心。LZ反思以及借鉴前辈们的经验。感觉我是时候去了解一下所谓的java设计模式。LZ最新版《大话设计模式》已经购入。后续会发布一些自己的心得。

步入正题。说一下LZ遇到的集合遍历删除的坑。

先看一段代码

package test;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class TestList {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        //第一种循环
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
        System.out.println("======我是分割线=========");
        //迭代器循环
        Iterator<Integer> it = list.iterator();
        while (it.hasNext()) {
        
            System.out.println(it.next());
        }
        System.out.println("======我是分割线=========");
        //增强for循环
        for (int i : list) {
        
            System.out.println(i);
        }

    }
}

上边代码运行结果自然不必说无论自学的还是培训大佬基本都知道这个是List的三种遍历方式。

现在LZ有个需求就是如果当List内的元素是2的时候将其从List中删除掉。LZ闭眼直接撸出了下边这段代码

for (int i : list) {
    if(i==2) {
        list.remove(i);
    }
    System.out.println(i);
}    

好的结束。信心满满的直接运行,顿时LZ愣了。

Exception in thread "main" java.util.ConcurrentModificationException
  at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
  at java.util.ArrayList$Itr.next(Unknown Source)  
  at test.TestList.main(TestList.java:27)

理论上这样写是没有问题的吧。哪里出了问题呢。反编译类之后看到源码

for (Iterator localIterator1 = list.iterator(); localIterator1.hasNext(); ) { int i = ((Integer)localIterator1.next()).intValue();
      if (i == 2) {
        list.remove(i);
      }
      System.out.println(i);
}

 根据反编译结果可以看到增强for循环的底层其实也是调用的List的迭代器。

 查询资料后可以看到Iterator是工作在一个独立的线程中,Iterator被创建之后会建立一个指向原来对象的单链索引表,当原来的对象被操作之后,这个索引表只会在迭代器创建的时候简历。所以咱们删除了List内部的元素引起索引指针往后移动的 时候就找不到要迭代的对象,所以按照 fail-fast 原则 Iterator 会马上抛出 java.util.ConcurrentModificationException 异常。 

所以在使用Iterator操作集合的时候不允许集合进行操作。但是索引表的建立和Iterator对象是相关联的。可以调用Iterator的remove方法直接删除。Iterator.remove() 方法会在删除当前迭代对象的同时维护索引的一致性。

因此LZ修改代码为

Iterator<Integer> it = list.iterator();
while (it.hasNext()) {
  int i = it.next();
  if(i==2) {
    it.remove();    
  }
}

 

OK 完美删除,解决问题。

posted @ 2019-07-13 18:16  雎鸠关关  阅读(223)  评论(0)    收藏  举报