`
andy136566
  • 浏览: 292886 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

List remove操作容易出现的问题

阅读更多

import java.util.*;

  public class object {

  public static void main(String[] args) {

  String str1 = new String("abcde");

  String str2 = new String("abcde");

  String str3 = new String("abcde");

  String str4 = new String("abcde");

  String str5 = new String("abcde");

  List list = new ArrayList();

  list.add(str1);

  list.add(str2);

  list.add(str3);

  list.add(str4);

  list.add(str5);

  System.out.println("list.size()=" + list.size());

  for (int i = 0; i < list.size(); i++) {

  if (((String) list.get(i)).startsWith("abcde")) {

  list.remove(i);

  }

  }

  System.out.println("after remove:list.size()=" + list.size());

  }

  }

  运行结果不是:

  list.size()=5

  after remove:list.size()=0

  居然是:

  list.size()=5

  after remove:list.size()=2

  原因:List每remove掉一个元素以后,后面的元素都会向前移动,此时如果执行i=i+1,则刚刚移过来的元素没有被读取。

  解决方法:

  1.倒过来遍历list

  for (int i = list.size()-1; i > =0; i--) {

  if (((String) list.get(i)).startsWith("abcde")) {

  list.remove(i);

  }

  }

  2.每移除一个元素以后再把i移回来

  for (int i = 0; i < list.size(); i++) {

  if (((String) list.get(i)).startsWith("abcde")) {

  list.remove(i);

  i=i-1;

  }

  }

  3.使用iterator.remove()方法删除

  for (Iterator it = list.iterator(); it.hasNext();) {

  String str = (String)it.next();

  if (str.equals("chengang")){

  it.remove();

  }

  }

  注意:在遍历list或者说在遍历集合过程中,执行了删除动作就会报错

  工作中碰到个ConcurrentModificationException。代码如下:

  List list = ...;

  for(Iterator iter = list.iterator(); iter.hasNext();) {

  Object obj = iter.next();

  ...

  if(***) {

  list.remove(obj);

  }

  }

  在执行了remove方法之后,再去执行循环,iter.next()的时候,报java.util.ConcurrentModificationException(当然,如果remove的是最后一条,就不会再去执行next()操作了)

  下面来看一下源码

  public interface Iterator<E> {

  boolean hasNext();

  E next();

  void remove();

  }

  public interface Collection<E> extends Iterable<E> {

  ...

  Iterator<E> iterator();

  boolean add(E o);

  boolean remove(Object o);

  ...

  }

  这里有两个remove方法

  接下来来看看AbstractList

  public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {

  //AbstractCollection和List都继承了Collection

  protected transient int modCount = 0;

  private class Itr implements Iterator<E> { //内部类Itr

  int cursor = 0;

  int lastRet = -1;

  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();

  }

  }

  public void remove() {

  if (lastRet == -1)

  throw new IllegalStateException();

  checkForComodification();

  try {

  AbstractList.this.remove(lastRet); //执行remove对象的操作

  if (lastRet < cursor)

  cursor--;

  lastRet = -1;

  expectedModCount = modCount; //重新设置了expectedModCount的值,避免了ConcurrentModificationException的产生

  } catch(IndexOutOfBoundsException e) {

  throw new ConcurrentModificationException();

  }

  }

  final void checkForComodification() {

  if (modCount != expectedModCount) //当expectedModCount和modCount不相等时,就抛出ConcurrentModificationException

  throw new ConcurrentModificationException();

  }

  }

  }

  remove(Object o)在ArrayList中实现如下:

  public boolean remove(Object o) {

  if (o == null) {

  for (int index = 0; index < size; index++)

  if (elementData[index] == null) {

  fastRemove(index);

  return true;

  }

  } else {

  for (int index = 0; index < size; index++)

  if (o.equals(elementData[index])) {

  fastRemove(index);

  return true;

  }

  }

  return false;

  }

  private void fastRemove(int index) {

  modCount++; //只增加了modCount

  ....

  }

  所以,产生ConcurrentModificationException的原因就是:

  执行remove(Object o)方法之后,modCount和expectedModCount不相等了。然后当代码执行到next()方法时,判断了checkForComodification(),发现两个数值不等,就抛出了该Exception。

  要避免这个Exception,就应该使用remove()方法。

  这里我们就不看add(Object o)方法了,也是同样的原因,但没有对应的add()方法。一般嘛,就另建一个List了

  下面是网上的其他解释,更能从本质上解释原因:

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

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

 

 

 

[转]http://www.qqread.com/java/2010/08/y495083.html

分享到:
评论

相关推荐

    List和Dictionary使用秘籍:C#集合操作中最容易犯的7个错误.pdf

    C#集合操作中关于List和Dictionary容易犯的错误以及解决方案: 一、错误一:在foreach循环中修改List 问题描述:在foreach循环遍历List时,如果尝试对List进行添加或删除元素等修改操作,会引发...

    前端项目-classlist.zip

    在不支持`classList`属性的老版本浏览器中,开发者通常需要使用字符串操作方法(如`className`的getter和setter)来管理类名,这种方式不仅繁琐且容易出错。`classList.js-master`项目就是为了解决这个问题,它提供...

    Java集合List详解

    通过流API,可以很容易地进行并行处理、延迟执行和其他复杂的操作。 在实际开发中,根据具体需求选择合适的List实现类是很重要的。例如,如果需要频繁地随机访问元素,应该选择ArrayList;如果需要在列表中间进行...

    标准C++的StringList

    在标准C++中,`StringList`的概念是对VCL(Visual ...这样的类设计允许用户方便地创建、操作和管理字符串列表,同时保持了与VCL中`TStringList`接口的相似性,使得从Delphi或其他VCL环境转到C++的开发者更容易适应。

    C语言-leetcode题解之19-remove-nth-node-from-end-of-list.c

    合理的命名、注释和代码结构可以让他人更容易理解你的代码。 15. 问题反馈和求助: 当遇到编程难题时,可以在LeetCode论坛或相关编程社区提问,获取他人的帮助和反馈。这是一个很好的学习机会,同时也可以帮助他人...

    list集合案例增、删、改、查,ArrayList与LinkedList的区别,LinkedList堆栈/队列的开发

    2. 删除(Remove):`remove(int index)`方法用于移除指定索引处的元素,而`remove(Object o)`方法则用于移除列表中首次出现的指定对象。 3. 修改(Modify):`set(int index, E element)`方法允许我们替换列表中...

    list,set,map,数组间的相互转换

    数组可以直接转换为`List`,但是需要注意的是,通过`Arrays.asList()`方法创建的`List`实际上是一个不可变的列表,也就是说它不支持`add()`或`remove()`等操作。如果需要一个可修改的`List`,可以通过以下方式实现:...

    stl 解决约瑟夫问题

    `std::list`不提供随机访问,但是其元素迭代器在删除操作后仍然有效,这使得我们可以在循环过程中更容易地进行操作。首先创建一个`std::list`,将所有人加入。然后,同样设置一个计数器,当计数达到指定值时,使用`...

    List实现 数据结构 模板

    双向链表每个节点不仅有指向下一个节点的指针,还有一个指向前一个节点的指针,这使得向前和向后遍历都变得容易。在C++中,我们可以扩展`Node`结构体,添加一个指向前一个节点的指针,并相应地更新`LinkedList`类: ...

    java.util.ConcurrentModificationException(解决方案).md

    在并发编程中,当多个线程同时访问和修改同一个集合时,很容易出现数据不一致的问题。其中一个典型的问题就是java.util.ConcurrentModificationException异常,通常在遍历集合时试图修改集合内容时抛出。Java提供了...

    Java程序员容易犯的10个错误

    正确做法是使用迭代器,但使用增强for循环(for-each)配合`list.remove()`会抛出`ConcurrentModificationException`。正确的迭代器移除方式如下: ```java Iterator&lt;String&gt; iter = list.iterator(); while ...

    Python中list循环遍历删除数据的正确方法

    然而,在遍历list的过程中删除数据是一个需要特别注意的操作,因为不当的处理很容易造成程序出错,例如IndexError: list index out of range异常。 在初学Python时,可能会遇到这样的问题:当我们试图在遍历list的...

    避坑手册 - JAVA编码中容易踩坑的十大陷阱.doc

    以下是一些在Java编码中容易出现的陷阱及其解决方法: 1. 循环中操作目标List: 当你需要在遍历List的同时删除元素时,直接使用for-each循环或者索引循环是错误的。这两种方式都会导致`...

    基于List, Set, Map, Integer, String, Tuple, Deque模块的C++库

    但是,为了达到Python的易用性,我们需要添加一些便捷的成员函数,比如`append`, `extend`, `insert`, `remove`, `pop`等,让开发者能够更直观地操作列表。 Set在C++中对应的类型是`std::set`,这是一个红黑树实现...

    C#中数组、ArrayList和List三者的区别详解及实例

    - **优点**:在声明List时需要指定元素类型T,这使得List成为类型安全的,避免了类型转换异常和不必要的装箱拆箱操作。 ```csharp List&lt;string&gt; list = new List(); list.Add("abc"); // 添加元素 list[0] = ...

    dom4j操作xml

    它提供了简单且直观的API,使得XML的解析、创建、查询和修改变得容易。在本文中,我们将深入探讨DOM4J如何进行XML操作,包括增加、删除和修改XML元素。 首先,我们需要理解XML的基本结构。XML(可扩展标记语言)是...

    操作系统课程算法设计

    remove_from_list(p); } else { adjust_partition(p, size); // 调整分区 } merge_adjacent_areas(); // 合并相邻空闲区 } ``` --- #### 设计2:哲学家就餐问题与死锁 **1. 哲学家就餐问题** - **问题描述*...

    Java平台提供了一个全新的集合框架.doc

    它提供了添加(add)、删除(remove)以及查询(size、isEmpty、contains)等基本操作。通过iterator()方法,可以获取一个迭代器,以便按顺序访问集合中的元素。Collection接口还包含了针对元素组的操作,如contains...

    数组的 用法

    列表提供了许多方便的方法,如Add、Remove、IndexOf、Insert等,使操作更灵活。例如,如果你想在列表末尾添加一个元素,可以使用Add方法;如果想删除某个元素,可以使用Remove方法。 数组和List在性能和使用场景上...

    c++ 模板 双向链表 操作

    双向链表是一种高级数据结构,它不仅在每个节点中存储数据,还包含指向前一个节点和后一个节点的指针,使得在链表中的前后移动变得容易。在本篇文章中,我们将深入探讨如何使用C++模板来实现双向链表及其操作,如...

Global site tag (gtag.js) - Google Analytics