package com.ljn.base;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* “对ArrayList进行遍历时,不要进行list.remove或者list.add操作”
* 我只是记住了这条规则,但规则的背后,是什么原理?
* 通过代码来说明(暂时不考虑多线程的情况):
*/
public class ConcurrentModificationExceptionTest {
public static void main(String[] args) {
remove(newArrayList(), "b");
remove(newArrayList(), "a");
}
private static void remove(List<String> list, String itemToRemove) {
Iterator<String> iterator = list.iterator();
int i = 1;
while (iterator.hasNext()) {
String item = iterator.next();
System.out.println(i++);
if (item.equals(itemToRemove)) {
list.remove(item);
System.out.println(item + " is removed");
} else {
System.out.println(item);
}
}
System.out.println();
}
private static List<String> newArrayList() {
List<String> list = new ArrayList<String>();
list.add("a");
list.add("b");
list.add("c");
return list;
}
/*
很简单的程序,输出如下:
1
a
2
b is removed
1
a is removed
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
at java.util.AbstractList$Itr.next(AbstractList.java:343)
为什么remove(newArrayList(), "a")会抛异常,而remove(newArrayList(), "b")就不会?
AbstractList里面有一个字段modCount,用来记录List“结构性改变”的次数,执行add或者remove都会使得modCount加1
AbstractList.iterator会创建一个AbstractList.Itr(内部类),而Itr备份了modCount,命名为expectedModCount
Itr执行next和remove操作前,都会调用checkForComodification方法检查expectedModCount是否等于modCount,如果不等,
就报ConcurrentModificationException
同时注意到Itr有一个指针cursor,表示下一个要遍历的元素的下标,初始值为0
Itr调用hasNext时,会将cursor与数组大小比较:
public boolean hasNext() {
return cursor != size();
}
回到例子
先看remove(newArrayList(), "a"):
1.newArrayList()之后,modCount=3
2.list.iterator()之后,Itr的expectedModCount=modCount=3
3.调用hasNext(),next(),再调用list.remove(item),此时modCount=4
4.调用hasNext(),next(),在next里面进行检查,发现expectedModCount不等于modCount,于是报错
再看remove(newArrayList(), "b"):
1.newArrayList()之后,modCount=3, list.size=3
2.list.iterator()之后,Itr的expectedModCount=modCount=3,cursor=0
3.调用hasNext(),next(),打印a,list.size=2,cursor=1;没有对list做修改,因此expectedModCount=3
4.调用hasNext(),next(),检查expectedModCount和modCount,都等于3,因此检查通过,b is removed;
此时cursor=2,size=2
5.调用hasNext,此时cursor 与 size()都等于2,hasNext返回false,循环结束
由此可见,remove(newArrayList(), "b")只是侥幸地成功运行了,但仍然是错误的用法
*/
}
分享到:
相关推荐
1. **原因分析**: - 当你使用`for-each`循环(增强for循环)遍历集合时,实际上底层是通过迭代器实现的。但是,如果你在循环内部直接修改集合(比如删除元素),而不是通过迭代器的`remove()`方法,那么迭代器将...
**原因分析:** 当使用迭代器遍历集合时,迭代器内部维护了一个指向集合的指针。如果在遍历过程中,集合结构发生了变化(如添加、删除或修改元素),迭代器无法预测这些变化,因此会抛出`...
**原因分析:** 在 Java 中,迭代器(Iterator)是用于遍历集合的专用工具,它提供了 `hasNext()` 和 `next()` 方法来依次访问集合中的元素。当使用迭代器时,集合类会保留一个修改计数器,记录对集合的修改次数。...
另外,CopyOnWriteArrayList无法保证实时性,Vector对于读写操作均加锁同步,可以保证读和写的强一致性,而CopyOnWriteArrayList由于其实现策略的原因,写和读分别作用在新老不同容器上,在写操作执行过程中,读不会...
同时,对于常见的bug,如并发修改异常(`ConcurrentModificationException`),我们需要理解其原因和避免方法,这通常涉及线程安全问题。 接着,我们转向`NIO`(New Input/Output),这是Java提供的一种I/O模型,相...
"新建 文本文档.txt"可能是文档列表中的一个文件,通常这种文件名表示未命名或初始状态的文本文件,可能包含报错大全的详细内容,如错误信息、错误原因分析和解决方法等。为了充分利用这份资源,建议打开该文件仔细...
分析LinkedList的迭代器实现,可以帮助我们了解并发修改异常(ConcurrentModificationException)的原因。 总之,Java开发者不仅需要扎实的语法基础,还要熟悉相关框架和中间件的使用,同时不断学习新的技术和最佳...
**5.2 问题:** "ConcurrentModificationException" **解决:** 在遍历集合时同时修改它会导致这个异常。解决方案包括: - **使用线程安全集合:** 使用线程安全的集合类,如`ConcurrentHashMap`。 - **迭代器的...
在Android开发过程中,遇到错误是常态,理解和解决这些错误对于开发者来说至关重要。...在实践中,通过日志调试、使用Android Studio提供的分析工具,以及学习更多Android SDK文档,可以更好地应对各种错误。
5. **迭代器失效**:在迭代过程中,如果修改了容器(添加、删除元素),可能导致ConcurrentModificationException。Java的迭代器设计为弱一致性的,这意味着在迭代过程中,如果其他线程改变了容器的结构,迭代器可能...
- 集合操作:add、remove、contains等方法的实现原理,以及并发修改异常(ConcurrentModificationException)的产生原因。 3. **多线程**: - 线程的创建:通过Thread类和Runnable接口两种方式创建线程。 - 线程...
- **原因分析**:在日期格式化中,“y”代表年份,而“Y”在某些日期格式化库中可能代表的是周年的年份,而非实际年份。为确保日期的一致性和准确性,阿里巴巴Java开发手册强调应明确指定使用“y”来表示实际年份,...
当你遇到“方法iterator()找不着的问题”时,这通常意味着你在尝试使用`iterator()`时遇到了错误,可能是由于以下几个原因: 1. **类库不兼容**: 如果你正在使用的类库版本不支持`iterator()`方法,比如你引用了...
下面将详细分析这些区别。 首先,从继承关系来看,HashMap和HashTable的起源不同。HashMap是Java 1.2引入的,它直接实现了Map接口,而HashTable则更早,它继承自Dictionary类。这种设计上的差异反映了HashMap的API...
当多个线程同时访问一个集合时,如果一个线程修改了集合,而另一个线程正在进行迭代,则抛出`ConcurrentModificationException`异常。 **39. 说说Hashtable 与 HashMap 的区别** - **线程安全性**:`Hashtable`...
- 在遍历集合时对其进行修改可能会导致`ConcurrentModificationException`异常。为了安全起见,在循环外部进行修改操作。 #### 四、异常处理 1. **合理捕获并处理异常** - 不应该忽视任何异常,而是应该适当地...
5. **并发问题**:在多线程环境中,如并发修改集合或共享资源时,可能会遇到`ConcurrentModificationException`或`InterruptedException`。应使用线程安全的数据结构或同步机制来避免这些问题。 6. **资源管理**:...