遍历

遍历一个list有三种方法

  • 使用索引来遍历(list.size()方法和list.get()方法)
  • 使用for each的形式遍历
  • 使用迭代器Iterator

删除

1、使用索引遍历,找到元素再删除

List<String> list = new ArrayList<String>();
  list.add("1");
  list.add("2");
  list.add("3");
  for(int i=0;i<list.size();i++){
    if(i==2){
      list.remove(i);
    }
  }

这种方式只适合删除单个元素,若想在一次遍历过程中删除多个元素,得不到正确结果,因为每删除一个元素,后面的元素都要向前移动,索引不再是以前的索引!

2、使用Iterator遍历时删除元素

List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        for (Iterator<Integer> it = list.iterator(); it.hasNext();) {
            int v = it.next();
            if (v == 2) {
                it.remove();
                //list.remove(v); //不推荐这种方法,会报错或出现逻辑错误
            }
        }

使用Iterator遍历元素时,想要删除元素应该使用iterator的remove()方法

3、使用for each遍历时删除元素

Exception in thread "main" java.util.ConcurrentModificationException

for each本质上还是在使用Iterator来遍历,遍历的同时去做修改是不正确的,就会发生上述异常。

Iterator源码分析

源码分析请看这里

Fail-Fast策略

在ArrayList,LinkedList,HashMap等的内部实现增,删,改中我们总能看到modCount的身影,modCount字面意思就是修改次数。大家有没有发现一个公共特点,所有使用modCount属性的全是线程不安全的。
modCount只有在本数据结构对应迭代器中才使用。

我们知道 java.util.HashMap 不是线程安全的,因此如果在使用迭代器的过程中有其他线程修改了map,那么将抛出ConcurrentModificationException,这就是所谓fail-fast策略

这一策略在源码中的实现是通过modCount域,modCount顾名思义就是修改次数,对HashMap内容的修改都将增加这个值,那么在迭代器初始化过程中会将这个值赋给迭代器的expectedModCount。在迭代过程中,判断 modCount跟expectedModCount是否相等,如果不相等就表示已经有其他线程修改了Map