`

ConcurrentModificationException迭代集合删除元素!!

阅读更多
ConcurrentModificationException主要原因及处理方法
2007年04月18日 星期三 12:57

当使用 fail-fast iterator 对 Collection 或 Map 进行迭代操作过程中尝试直接修改 Collection / Map 的内容时,即使是在单线程下运行,   java.util.ConcurrentModificationException 异常也将被抛出。

  Iterator 是工作在一个独立的线程中,并且拥有一个 mutex 锁。 Iterator 被创建之后会建立一个指向原来对象的单链索引表,当原来的对象数量发生变化时,这个索引表的内容不会同步改变,所以当索引指针往后移动的时候就找不到要迭代的对象,所以按照 fail-fast 原则 Iterator 会马上抛出 java.util.ConcurrentModificationException 异常。

  所以 Iterator 在工作的时候是不允许被迭代的对象被改变的。但你可以使用 Iterator 本身的方法 remove() 来删除对象, Iterator.remove() 方法会在删除当前迭代对象的同时维护索引的一致性。

有意思的是如果你的 Collection / Map 对象实际只有一个元素的时候, ConcurrentModificationException 异常并不会被抛出。这也就是为什么在 javadoc 里面指出: it would be wrong to write a program that depended on this exception for its correctness: ConcurrentModificationException should be used only to detect bugs.

把HashMap改成ConcurrentHashMap
也可以

1
 public class MapIterator {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
begin();
	}
	public static void begin()
	{
		ConcurrentHashMap hm2
		=new ConcurrentHashMap();
		HashMap<Integer,String> hm=new HashMap<Integer,String>();
		char[] c=new char[1];
		c[0]='a';
		for(int i=0;i<10;i++)
		{
			c[0]+=i;
			 hm.put(new Integer(i),new String(c));
		}
		Set s=hm.entrySet();
		Iterator i=s.iterator();
		Map.Entry<Integer,String>  me;
		while(i.hasNext())
		{
			me=(Map.Entry<Integer,String>)i.next();
			//System.out.println(me.getKey());
			System.out.println(hm.size());
			hm.remove(me.getKey());
		}
		System.out.println(hm.size());
	}

}

先用HashMap进行操作!!
爆异常
java.util.HashMap$EntryIterator.next-----(积累----可以通过报异常 查看调用的什么方法!!)

(这里就是访问的HashMap内部类的EntryIterator)

看下源码

Form HashIterator
 public void remove() 
{
            if (current == null)
                throw new IllegalStateException();
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            Object k = current.key;
            current = null;
            HashMap.this.removeEntryForKey(k);
            expectedModCount = modCount;
}



expectedModCount是为实现fail-fast实现的变量!
如果直接用HashMap中的方法删除的话 会导致两个变量不相等!!

看下ConcurrenthashMap中的Iterator实现

 
public void remove()
 {
            if (lastReturned == null)
                throw new IllegalStateException();
            ConcurrentHashMap.this.remove(lastReturned.key);
            lastReturned = null;
        }

上面来自类  ConcurrentHashMap内部类
private abstract class HashIterator<E> implements Iterator<E>
可以看出

直接调用ConcurrentHashmap的removal方法!!
所以也没有研究的必要了

下面是 fail-fast的介绍

An iterator is considered fail-fast if it throws a ConcurrentModificationException under either of the following two conditions:
1.In multithreaded processing: if one thread is trying to modify a Collection while another thread is iterating over it.

2.In single-threaded or in multithreaded processing: if after the creation of the Iterator, the container is modified at any time by any method other than the Iterator's own remove or add methods.
Note in particular what is implied by the second condition: After we create a container's iterator, during a loop that iterates over the container we must only use the remove (and when applicable add) methods defined for the iterator and that we must NOT use the same methods defined for the container itself. To illustrate this point, suppose we declare and initialize a List in the following manner

List list = new ArrayList();
list.add("Peter");
list.add("Paul");
list.add("Mary");

Let's say we wish to iterate over this list. We'd need to declare a ListIterator as follows:
ListIterator iter = list.listIterator();

Having created this iterator, we could now set up a loop like:
while(iter1.hasNext()){
String str = iter1.next();
// do something with str
}

Because iter is fail-fast, we are not allowed to invoke List's add or remove methods inside the loop. Inside the loop, we are only allowed to use ListIterator's add and remove methods. This makes sense because it is the Iterator object that knows where it is in a List as the List is being scanned. The List object itself would have no idea of that.
The Iterators supported by all the work-horse container classes, such as ArrayList, LinkedList, TreeSet, and HashSet, are fail-fast. The Iterator type retrofitted to the older container class Vector is also fail-fast. For associative containers, such as HashMap and the older HashTable, the Iterator type for the Collections corresponding to either the keys or the values or the <key, value> pairs are fail-fast with respect to the container itself. That means that even if you are iterating over, say, just the keys of the container, any illegal concurrent modifications to the underlying container would be detected.

One final note regarding iterators versus enumerations: It is also possible to use an Enumeration object returned by the elements() method for iterating over the older container types such as Vector. However, Enumerations do not provide a fail-fast method. On the other hand, the more modern Iterator returned by a Vector's iterator() and listIterator() methods are fail-fast. Hence, iterators are recommended over enumerations for iterating over the elements of the older container types.

继承自AbstractList的List:LinkedList,ArrayList,Vector,Stack的Iterator都有这种属性
PriorityQueue的Iterator也有这种属性.

另外抽象类AbstractCollection 是集合的父母!
他的子类都实现了iterator()方法获得这个迭代集合!所以
他的子类1!
AbstractList, AbstractQueue, AbstractSet, ArrayDeque
以及他的子类都会有此特性!!
例如 TreeSet,
    中种特性的实现方法是借助于一个modCount成员变量,记录structual modification的次数,在Iterator初始化时,让它的成员变量expectedModCount等于modCount,这样在Iterator做遍历时,如果发现 expectedModCount!= modCount就说明容器的内容发生了改变,抛出ConcurrentModificationException异常
分享到:
评论

相关推荐

    遍历并批量删除容器中元素出现ConcurrentModificationException原因及处置

    当你在一个迭代器正在遍历集合的过程中添加或删除元素时,就会抛出这个异常。这个问题在单线程环境下不会出现,但在多线程并发场景下,如果多个线程同时修改一个集合,就可能导致`ConcurrentModificationException`...

    使用Iterator接口遍历集合元素

    2. 当使用 Iterator 来迭代访问 Collection 集合元素时,Collection 集合里的元素不能被改变,只有通过 Iterator 的 remove 方法来删除上一次 next 方法返回的集合元素才可以。否则将会引发 java.util....

    30个Java经典的集合面试题!.zip

    如果在迭代过程中集合结构被修改(如添加、删除元素),迭代器会抛出ConcurrentModificationException。 28. **什么是Java的并发容器?** 如ConcurrentHashMap、ConcurrentLinkedQueue等,它们设计用于并发环境,...

    hashMap利用iterator迭代器迭代元素方法

    当我们需要遍历`HashMap`中的所有元素时,通常会使用`Iterator`接口,它是Java集合框架的一部分,提供了对集合的迭代访问。 `Iterator`接口定义了三个基本方法:`hasNext()`、`next()`和`remove()`。`hasNext()`...

    在list集合中输入元素,去除重复的元素并输出

    根据给定文件的信息,本文将详细介绍如何在Java的List集合中去除重复元素的三种方法:使用for循环、使用迭代器以及利用HashSet的特性。 ### 一、使用for循环去除重复元素 这种方法的基本思想是通过双重循环来遍历...

    collecter集合总结

    需要注意的是,在迭代的过程中不能对集合中元素进行修改,否则将产生 java.util.ConcurrentModificationException。 选择集合 在实际开发中,选择合适的集合是非常重要的。以下是一些选择集合的建议: * 如果需要...

    java 迭代及迭代器的小例子

    例如,在java.util.concurrent包中的ConcurrentLinkedQueue等线程安全的集合类,它们的迭代器是弱一致性(weakly consistent)的,这意味着迭代器不会抛出ConcurrentModificationException,但可能不反映对集合的...

    java基础 集合-22-迭代器设计模式

    通过迭代器,我们可以方便地遍历集合,添加、删除或修改元素,同时保持了集合的封装性。 1. **迭代器接口** Java中的`Iterator`接口位于`java.util`包下,它是所有迭代器的基类。它提供了三个核心方法: - `...

    迭代器的用法

    在迭代器遍历过程中,对集合进行修改(如添加或删除元素)可能会影响迭代器的行为。例如,如果在调用`next()`之后调用`remove()`,则通常不会有问题。但是,在调用`hasNext()`或`next()`之前删除元素可能会导致...

    迭代器Iterator.txt

    迭代器模式是一种常用的设计模式,它允许我们以一种顺序访问集合对象的方式遍历其元素,而无需暴露该对象的内部表示。接下来,我们将详细讨论这一模式的关键概念、结构及其在Java集合框架中的具体实现。 ### 一、...

    Iterator迭代器讲解

    1. **耦合性问题**:如果直接使用索引或者迭代来访问集合中的元素,那么访问代码会与集合本身的实现紧密耦合,不利于后期的修改和扩展。 2. **代码重复**:每种集合类型通常都有其特定的遍历方式,这导致客户端...

    java Iterator迭代器的使用

    迭代器的主要方法包括`hasNext()`(检查集合中是否存在下一个元素)、`next()`(返回当前元素并移动到下一个)和`remove()`(删除当前元素)。 2. **获取迭代器** 要使用`Iterator`,首先需要通过调用集合类的`...

    java集合类遍历的同时如何进行删除操作.docx

    当我们尝试在循环遍历集合(如ArrayList)时删除元素,Java的默认行为是不允许的,因为这违反了迭代器的协定。迭代器通常需要在删除元素时通过自身的`remove()`方法来确保内部状态的一致性。如果直接调用集合的`...

    Iterator遍历过程中list删除导致异常

    当你想在迭代过程中删除元素时,应该使用`Iterator`的`remove()`方法,而不是直接调用集合的`remove()`方法。这样,`Iterator`会正确处理集合的内部状态,避免异常: ```java List&lt;String&gt; list = new ArrayList...

    day05-集合1

    - `clear()`:删除集合中的所有元素,使其大小变为0。 - `contains(Object o)`:如果集合包含指定元素,则返回`true`。 - `isEmpty()`:如果集合为空,返回`true`。 - `size()`:返回集合中元素的数量。 4. **...

    forEach中为什么不能删除元素解决方案.pdf

    然而,在使用`forEach`循环时,直接尝试修改遍历的集合(例如删除或添加元素)是不被允许的,因为这会导致`ConcurrentModificationException`异常。本文将探讨在使用`forEach`循环中遇到无法删除元素的问题,并提供...

    java集合框架全面进阶.pdf

    Collection接口提供了一些基本操作,比如添加、移除、检查元素是否存在于集合中、获取集合大小、判断集合是否为空以及迭代集合中的元素。 5. **Iterator接口**:Iterator接口用于提供一种顺序遍历集合中元素的方式...

    java 集合并发操作出现的异常ConcurrentModificationException

    当迭代器检测到集合在迭代过程中被修改,它会立即抛出`ConcurrentModificationException`,而不是尝试继续迭代或返回不确定的结果。 在描述中提到了Map的遍历方式,通常我们会通过获取Map的键集(keySet)或值集...

    集合类框架专题

    - 迭代器是遍历集合元素的主要工具,提供了`hasNext()`检查是否还有元素,`next()`返回下一个元素,以及`remove()`删除当前元素的方法。 - 迭代器是故障快速修复(fail-fast)的,意味着如果在迭代过程中底层集合被...

    内置迭代器的linked list例题

    此外,如果在迭代期间直接修改集合(如使用`add()`、`remove()`等方法,而不是通过迭代器),可能会导致`ConcurrentModificationException`,因为这破坏了迭代器的内部状态。 总结来说,Java的LinkedList与内置迭代...

Global site tag (gtag.js) - Google Analytics