上次去一个公司的笔试题里面有一个关于ArrayList的remove问题,今天突然想到以前遇到的ConcurrentModificationException问题,所以在此总结一下,以便以后查阅。
对ArrayList的操作我们可以通过索引象来访问,也可以通过Iterator来访问,只要不对ArrayList结构上进行修改都不会造成ConcurrentModificationException,单独用索引对ArrayList进行修改也不会造成该问题,造成该问题主要是在索引和Iterator混用。可以通过JDK的源码来说明该问题。
首先看下AbstractList的Iterator内的代码:
/**
*在Iterator的内部有个expectedModCount 变量,
*该变量每次初始化*Iterator的时候等于ArrayList的modCount,modCount记录了对ArrayList的结构修改次数,
*在通过Iterator对ArrayList进行结构的修改的时候都会将expectedModCount 与modCount同步,
*但是如果在通过Iterator访问的时候同时又通过索引的方式去修改ArrayList的结构的话,
*由于通过索引的方式只会修改modCount不会同步修改expectedModCount 就会导致
*modCount和expectedModCount 不相等就会抛ConcurrentModificationException,
*这也就是Iterator的fail-fast,快速失效的。所以只要采取一种方式操作ArrayList就不会出问题,
*当然ArrayList不是线程安全的,此处不讨论对线程问题。
*
*/
int expectedModCount = modCount;
public E next() {
checkForComodification();//判断modeCount与expectedModCount 是否相等,如果不相等就抛异常
try {
E next = get(cursor);
lastRet = cursor++;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
public void remove() {
if (lastRet == -1)
throw new IllegalStateException();
checkForComodification();//同样去判断modeCount与expectedModCount 是否相等
try {
AbstractList.this.remove(lastRet);
if (lastRet < cursor)
cursor--;
lastRet = -1;
expectedModCount = modCount;//此处修改ArrayList的结构,所以要将expectedModCount 于modCount同步,主要是AbstractList.this.remove(lastRet);的remove方法中将modCount++了,导致了modCount与expectedModCount 不相等了。
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
//判断modCount 与expectedModCount是否相等,如果不相等就抛ConcurrentModificationException异常
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
故我的结论是:对ArrayList的操作采用一种遍历方式,要么索引,要么Iterator别混用即可。
下面是网上看见别人的解释:写道
Iterator 是工作在一个独立的线程中,并且拥有一个 mutex 锁。 Iterator 被创建之后会建立一个指向原来对象的单链索引表,当原来的对象数量发生变化时,这个索引表的内容不会同步改变,所以当索引指针往后移动的时候就找不到要迭代的对象,所以按照 fail-fast 原则 Iterator 会马上抛出 java.util.ConcurrentModificationException 异常。
所以 Iterator 在工作的时候是不允许被迭代的对象被改变的。但你可以使用 Iterator 本身的方法 remove() 来删除对象, Iterator.remove() 方法会在删除当前迭代对象的同时维护索引的一致性
分享到:
相关推荐
Java.util.ConcurrentModificationException 异常问题详解 ConcurrentModificationException 异常是 Java 中一个常见的异常,它发生在 Iterator 遍历集合时,集合同时被修改引起的异常。在 Java 中,集合类如 ...
然而,给出的部分内容却是一段关于导入Excel文件到Web应用中的代码片段,并没有直接涉及List的具体操作方法。因此,我们将结合标题与描述,围绕Java中List的相关知识点进行详细阐述。 ### Java中的List概述 在Java...
在Java编程中,`Iterator`是用于遍历集合(如`List`, `Set`等)的重要工具。然而,当我们尝试在使用`Iterator`遍历集合的过程中直接修改集合时,可能会遇到`ConcurrentModificationException`异常。这个异常通常发生...
bject[initialCapacity]; } else if (initialCapacity == 0) {...同时,需要注意在并发环境下使用ArrayList可能会遇到`ConcurrentModificationException`,应当避免在遍历过程中修改集合,或者选择线程安全的数据结构。
在Java编程中,`ConcurrentModificationException`是一个常见的运行时异常,主要出现在多线程环境下对集合类(如List、Set、Map等)进行并发修改时。然而,这个异常不仅限于多线程环境,即使在单线程中,如果在遍历...
如果在遍历过程中需要移除元素,必须使用`it.remove()`,直接调用`list.remove()`会导致并发修改异常(ConcurrentModificationException)。 ### 第二种:增强型for循环(foreach) ```java for (A a : list) { /...
迭代器是Java集合框架提供的一种统一的遍历方式,它可以安全地删除元素,而不会抛出ConcurrentModificationException。以下是一个使用迭代器移动元素的例子: ```java List<String> list = new ArrayList(); // ...
根据给定文件的信息,本文将详细介绍如何在Java的List集合中去除重复元素的三种方法:使用for循环、使用迭代器以及利用HashSet的特性。 ### 一、使用for循环去除重复元素 这种方法的基本思想是通过双重循环来遍历...
使用 ListIterator 时,需要注意并发修改异常(ConcurrentModificationException),当在迭代过程中直接通过集合对象修改集合时,可能会抛出此异常。因此,推荐在迭代时仅使用迭代器的方法来操作元素。 总的来说,...
HashMap和List遍历方法及如何遍历删除元素总结 HashMap和List都是Java中最常用的数据结构,它们都可以用来存储和操作数据。然而,在遍历和删除元素时,需要小心地处理,以免出现问题。下面总结了HashMap和List的...
4. **迭代器与List的关系**:在处理List时,迭代器是首选的遍历方式,因为它提供了一种安全的、避免并发修改异常(ConcurrentModificationException)的方式。当在一个线程中使用迭代器遍历List,而在另一个线程中...
使用迭代器的主要好处是可以安全地修改集合,因为它在遍历时不会抛出`ConcurrentModificationException`异常。 - `hasNext()`:判断集合中是否有更多的元素。 - `next()`:返回集合中的下一个元素。 - `remove()`:...
ListIterator 可以用于迭代 List 集合中的元素,但是在迭代时,不可以通过集合对象的方法操作集合中的元素,因为会发生 ConcurrentModificationException 异常。 以下是 List 集合的常用方法的详解: 1.add(int ...
- **迭代器(Iterator)**:`Iterator`是Java集合框架的一部分,它提供了一种安全的方式来遍历集合,可以方便地进行添加、删除操作,且不会抛出`ConcurrentModificationException`。示例代码中的`it.hasNext()`和`...
在直接遍历 List 集合元素时增加、删除元素会报错,例如使用 for 循环遍历 List 时删除元素,会抛出 ConcurrentModificationException 异常。解决方法是使用 Iterator 迭代器遍历 List,并使用 Iterator 的 remove ...
此外,如果在迭代期间直接修改集合(如使用`add()`、`remove()`等方法,而不是通过迭代器),可能会导致`ConcurrentModificationException`,因为这破坏了迭代器的内部状态。 总结来说,Java的LinkedList与内置迭代...
在Java编程中,遍历并删除集合(如List或Set)中的元素是一项常见的操作,但如果不正确地执行,可能会导致`ConcurrentModificationException`异常。这个异常通常在尝试修改正在迭代的集合时出现,因为Java的集合迭代...
此外,foreach循环不能在遍历过程中修改集合,否则会抛出`ConcurrentModificationException`异常。 3. **索引遍历** 最传统的方式是通过索引来遍历List集合,这种方式适用于需要访问元素索引的情况: ```java ...
这个示例中,会抛出 ConcurrentModificationException 异常,因为在遍历 list 时,不能修改 list 的内容。为了解决这个问题,可以使用 CopyOnWriteArrayList,避免这种异常: ```java CopyOnWriteArrayList<String>...
使用For-each循环遍历List时,不能直接在循环体中调用`remove()`方法,因为这会导致`ConcurrentModificationException`。这是因为For-each循环内部使用的是迭代器,而迭代器不支持在遍历过程中直接修改集合。示例...