`

List 迭代过程中删除或添加元素

阅读更多
1. List 迭代过程中删除元素采用list.remove(obj)会造成其size自减,modCount自增而产生问题:

Collection list = new ArrayList();
list.add("creek");
list.add("misty");
list.add("forest");

Iterator iter = list.iterator();

while (iter.hasNext()) {
    Object obj = iter.next();
    if ("creek".equals(obj))
       list.remove(obj);
}


运行时会报java.util.ConcurrentModificationException错误。

我们先看看java.util.ArrayList的相关代码:

private void fastRemove(int index) {
	modCount++;
	int numMoved = size - index - 1;
	if (numMoved > 0)
		System.arraycopy(elementData, index+1, elementData, index,
						 numMoved);
	elementData[--size] = null; // Let gc do its work
}


还有java.util.AbstractList的:

int expectedModCount = modCount;

public boolean hasNext() {
		return cursor != size();
}

public E next() {
		checkForComodification();
	try {
	E next = get(cursor);
	lastRet = cursor++;
	return next;
	} catch (IndexOutOfBoundsException e) {
	checkForComodification();
	throw new NoSuchElementException();
	}
}

final void checkForComodification() {
	if (modCount != expectedModCount)
	throw new ConcurrentModificationException();
}


当调用iterator()方法的时候,expectedModCount = modCount (等于3,三次添加元素)
第一次循环的时候,把第一个元素给删除了,这时modCount++ (等于4)
第二次调用iter.next()会调用checkForComodification()方法,check到这两个值不相等,所以报异常。

如果我们需要在迭代的过程中对元素进行删除操作,使用AbstractList提供的remove()方法,当然这也不是线程安全的:

	public void remove() {
	    if (lastRet == -1)
		throw new IllegalStateException();
            checkForComodification();

	    try {
		AbstractList.this.remove(lastRet);
		if (lastRet < cursor)
		    cursor--;
		lastRet = -1;
		// Let the values of the two variables be equal
		// so that the check would be passed
		expectedModCount = modCount;
	    } catch (IndexOutOfBoundsException e) {
		throw new ConcurrentModificationException();
	    }
	}


可以看到这里的expectedModCount已经设置成和modCount相等了。

另一种方式是利用并发库里的CopyOnWriteArrayList类:
Collection list = new CopyOnWriteArrayList();
...


2. List 迭代过程中添加元素采用list.add(obj)会造成其size自增,modCount自增而产生问题,这里就不再赘述。
分享到:
评论

相关推荐

    SafeList:一个在迭代时处理添加和删除元素的列表

    在当前循环位置之后添加元素时,循环将继续进行处理,就好像它们从一开始就存在一样。 当前循环位置之前添加的元素将被跳过。 在当前循环位置之前删除的元素不会对循环产生影响。 在当前循环位置之后删除的元素将...

    collecter集合总结

    * add(Object obj):向集合中添加元素 obj。 * clear():清空集合中的所有元素。 * contains(Object obj):判断集合中是否包含指定元素 obj。 * size():返回集合中元素的个数。 * isEmpty():判断集合中是否...

    Java集合Collection、List、Set、Map使用详解.doc

    * add(E e):添加元素到集合中 * remove(Object o):从集合中删除元素 * contains(Object o):检查元素是否在集合中 * size():返回集合的大小 1.2.2 迭代器 迭代器是集合框架中的一个重要概念。它允许我们遍历...

    实验05 Java集合.doc

    3)把集合中的元素打印出来(使用迭代器Iterator) 2、编写程序练习List集合的基本使用: 1) 创建一个只能容纳String对象名为names的ArrayList集合; 2)按顺序往集合中添加5个字符串对象:"张三"、"李四"、"王五...

    C++queue介绍及详细使用示例(源代码)

    deque 通常在需要快速地在两端添加或移除元素,但又不想承受像 list 那样的额外开销时使用。 deque的主要特性: 双端操作:deque 允许在序列的前端和后端快速插入和删除元素。 内部引用:deque 通常不会将所有元素...

    java集合详解.pdf

    * addAll(Collection&lt;? extends E&gt; c):添加一个集合中的所有元素到当前集合中。 * remove(Object o):删除集合中的一个元素。 * removeAll(Collection&lt;?&gt; c):删除集合中的所有元素。 * clear():清空集合中的所有...

    container-doublylist:JavaScript中的DoublyList实现

    要添加元素: var myObjectReference = myList . add ( myObject ) ; // add on front by default// orvar myObjectReference = myList . addFront ( myObject ) ;// orvar myObjectReference = myList . addBack ...

    JDKAPI18CN(中文版)

    没有规定增长政策的细节,除了添加元素具有不变的摊销时间成本。 应用程序可以添加大量使用ensureCapacity操作元件的前增大ArrayList实例的容量。 这可能会减少增量重新分配的数量。 请注意,此实现不同步。 ...

    Java语言的Util类详细介绍

    List还提供一个listIterator()方法,返回一个ListIterator接口,和标准的Iterator接口相比,ListIterator多了一些add()之类的方法,允许添加、删除、设定元素,还能向前或向后遍历。 实现List接口的常用类有...

    ArrayList.java

    增长政策的细节无法超越的事实,添加元素具有恒定的摊余成本的时间规定。 应用程序可以添加大量使用的ensureCapacity操作元件的前增大ArrayList实例的容量。 这可以减少增量再分配的数量。 注意,此实现不是同步的...

    集合框架练习.doc

    通过使用 listIterator() 方法可以获取 List 集合的迭代器,然后使用 hasNext() 方法检查是否还有下一个元素,如果有,则使用 next() 方法获取该元素,并使用 equals() 方法检查是否等于"abc",如果等于,则使用 ...

    STL读书笔记

    a.begin()迭代器返回指向容器中第一个元素的迭代器;a.end()迭代器返回超尾值迭代器;a.size()UINT返回容器中的元素个数,等价于a.end() – a.begin();a.swap(b)void交换a和b的内容;a = = b可转换为BOOL如果a和b...

    java集合-练习题.pdf

    Collection 接口提供了基本的集合操作,如添加、删除、遍历等。 二、List 接口的特点 List 接口是 Collection 接口的子接口,它的特点是元素是有顺序的,可以重复的。List 接口提供了根据索引访问元素的方法,如 ...

    Java系列ArrayList

    ArrayList ArrayList 类是一个可以动态修改的数组,与普通数组的区别就是它是没有固定大小的限制,...添加元素 访问元素 修改元素 删除元素 计算大小 迭代数组列表 其他的引用类型 ArrayList 排序 Java ArrayList 方法

    习----题-Java-Web程序设计教程-[共2页].pdf

    } 其中,第 行部分在定义 List 时定义了泛型,保证 List 中的元素都是 Student 类型。因此在 第 行部分在取出 List 中的元素时就不需要再强制转换了。 另外,在前面的比较接口中也可以使用泛型,例如例 3.3 的 ...

    测量程序编制 - python 41数据类型:dict(字典)-函数(四).pptx

    返回一个迭代器,可以使用 list() 来转换为列表 8 dictname.setdefault(key, default=None) 和get()类似, 但如果键不存在于字典中,将会添加键并将值设为default 9 dictname.update(dict2) 把字典dict2的键/值对更新...

    测量程序编制 - python 42数据类型:dict(字典)-函数(五).pptx

    返回一个迭代器,可以使用 list() 来转换为列表 8 dictname.setdefault(key, default=None) 和get()类似, 但如果键不存在于字典中,将会添加键并将值设为default 9 dictname.update(dict2) 把字典dict2的键/值对更新...

    测量程序编制 - python 39数据类型:dict(字典)-函数(二).pptx

    返回一个迭代器,可以使用 list() 来转换为列表 8 dictname.setdefault(key, default=None) 和get()类似, 但如果键不存在于字典中,将会添加键并将值设为default 9 dictname.update(dict2) 把字典dict2的键/值对更新...

    Java 容器.pdf_电子版pdf版

    ArrayList 的默认大小为 10,添加元素时使用 ensureCapacityInternal() 方法来保证容量足够,如果不够时,需要使用 grow() 方法进行扩容,新容量的大小为 oldCapacity + (oldCapacity &gt;&gt; 1) ,也就是旧容量的 1.5 倍...

Global site tag (gtag.js) - Google Analytics