遍历
遍历一个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