Java基础 List中元素的遍历删除
遍历删除List中元素时,有时会出现意想不到得错误
——使用普通for循环删除元素
public static void main(String[] args)
{
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 5; i++)
list.add(i);
for (int i = 0; i < list.size(); i++)
{
list.remove(i);
}
for (int i = 0; i < list.size(); i++)
{
System.out.println(list.get(i));
}
}
希望 通过list.remove(index)方式删除所有元素 但结果显示只删除了部分元素
删除元素后,会该变其它元素的index下标 以及整个list的Size大小
——通过增强for循环删除元素
public static void main(String[] args)
{
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 5; i++)
list.add(i);
for (Integer num : list)
if (num % 2 == 0)
list.remove(num);
}
运行时直接出现 ConcurrentModificationException异常
原因分析: 增强for循环内部采用 Iterator迭代器进行遍历
fail-fast 机制是java集合(Collection)中的一种错误机制。当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件。例如:当某一个线程A通过iterator去遍历某集合的过程中,若该集合的内容被其他线程所改变了;那么线程A访问集合时,就会抛出ConcurrentModificationException异常,产生fail-fast事件。当方法检测到对象的并发修改,但不允许这种修改时就抛出该异常。同时需要注意的是,该异常不会始终指出对象已经由不同线程并发修改,如果单线程违反了规则,同样也有可能会抛出该异常。
Iterator是工作在一个独立的线程中,并且拥有一个 mutex 锁。 Iterator被创建之后会建立一个指向原来对象的单链索引表(副本),当原来的对象数量发生变化时,这个索引表的内容不会同步改变,所以当索引指针往后移动的时候就找不到要迭代的对象,所以按照 fail-fast 原则 Iterator 会马上抛出java.util.ConcurrentModificationException异常。
——通过Iterator迭代器循环删除(正确做法)
public class List_test {
public static void main(String[] args)
{
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 5; i++)
list.add(i);
Iterator<Integer> it=list.iterator();
while(it.hasNext())
{
if(it.next()%2==0)
it.remove();
}
}
}
Iterator迭代器可以认为是一个由头指针开始的访问指针,即使删除元素,依旧保持勾连状态,可以正确删除List中元素
对于多线程中的线程安全的CopyOnWriteArrayList
其Iterator只是原先数据的一个副本快照,所有不能采用Iterator迭代器遍历删除,而可以采用增强For循环(与普通ArrayList不同)
——正确做法
public class Main {
public static void main(String[] args) throws Exception {
List<Integer> list = new CopyOnWriteArrayList<>();
for (int i = 0; i < 5; i++)
list.add(i);
for (Integer num : list)
if (num % 2 == 0) {
list.remove(num);
}
}