`
codsoul
  • 浏览: 212443 次
  • 性别: 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() 方法会在删除当前迭代对象的同时维护索引的一致性。
分享到:
评论

相关推荐

    list用remove实现结构体成员的删除

    总结一下,使用 `list` 的 `remove()` 方法删除结构体成员(对象实例)的关键在于找到要删除的元素,并根据其唯一的标识进行匹配。在实际编程中,我们还需要考虑到异常处理和对原列表的影响,以及选择合适的删除策略...

    基于C++ list中erase与remove函数的使用详解

    这篇文章主要讲解了`list`中的两个重要成员函数`erase`和`remove`,它们用于删除链表中的元素。理解这两个函数的正确用法对于编写高效且无误的C++代码至关重要。 首先,`erase`函数的主要作用是删除链表中特定位置...

    Java list.remove( )方法注意事项

    `remove()`方法是`List`接口中的一个关键操作,用于从列表中删除指定的元素或根据索引移除元素。在使用`remove()`方法时,需要注意一些重要的细节以避免潜在的问题和错误。下面将详细介绍`List.remove()`方法的两种...

    STL中list的使用

    在标准模板库(STL)中,`list`容器是一个双向链表,它提供了高效的插入和删除操作,尤其是在列表的头部或尾部进行操作时,性能尤为突出。下面将详细介绍`list`的各种功能和用法。 ### 创建和初始化 1. **空list**...

    RemoveDrive卸载U盘或者移动硬盘

    使用RemoveDrive要注意的是,尽管它能快速卸载设备,但并不意味着数据安全就得到了保障。在执行卸载命令前,应确保没有正在进行的读写操作,否则可能会导致数据丢失。此外,对于不熟悉命令行操作的用户,建议在了解...

    【Robotframework】列表List的常用操作.pdf

    需要注意的是,Robot Framework中的列表用`@{list}`表示,而Python中用`$list`。比较两个`@{list}`时,直接写成`$list`。例如,`Lists Should Be Equal ${list} ${list1}` 判断两个列表是否相同。 8. **Remove From...

    C#遍历List并删除某个元素的方法

    当我们需要遍历List并根据条件删除特定元素时,需要注意正确的方法,以避免在遍历过程中出现错误。以下将详细介绍如何在C#中遍历List并删除元素,包括正序和倒序遍历的技巧。 首先,我们来看一下错误的遍历方式。...

    测试listBox1.Items.Remove是用ToString还是利用gethashCode来定位元素的

    在`ListBox`中,如果你要根据对象的字符串表示来查找并移除元素,可以遍历`Items`集合,将每个元素转换为字符串,然后与目标字符串比较。如果匹配,则调用`Remove`方法移除该元素。这种方法适用于元素是自定义类且...

    Python列表list内建函数用法实例分析【insert、remove、index、pop等】

    `insert()`函数允许我们在列表中的任意位置插入一个新元素,而`remove()`函数则用于移除列表中的指定元素。 ##### `insert(index, obj)` - **功能**:将对象`obj`插入到列表的`index`位置。注意这里的`index`是基于...

    C# List用法

    C# List是.NET Framework...需要注意的是,虽然List提供了强大的功能,但是性能问题仍然是开发者需要注意的地方,尤其是当处理大量数据且数据类型为值类型时。合理选择数据结构和方法,可以有效避免不必要的性能开销。

    C# List用法详解

    需要注意的是,在选择使用IList还是ArrayList类时,需要考虑性能和类型安全性问题。如果使用IList,那么在大多数情况下执行速度更快、更加类型安全,但如果使用值类型,则需要考虑装箱和取消装箱问题。

    两个list比较 取不同的对象

    ### 两个List比较取不同的对象 在Java编程中,经常需要对两个`List`进行比较,...最后,正确使用迭代器可以在遍历过程中避免并发修改异常等问题。在实际应用中,可以根据具体需求灵活调整这些技术点,以达到最佳效果。

    C#_StringList的用法

    aList.Remove("a"); 结果为 bcde。 2. public virtual void RemoveAt(int index):移除 ArrayList 的指定索引处的元素。 例如:ArrayList aList = new ArrayList(); aList.Add("a"); aList.Add("b"); aList.Add("c...

    两个集合比较(新增、编辑、删除)

    如果在多线程环境中操作集合,需要注意线程安全问题,可能需要使用`CopyOnWriteArrayList`或其他同步容器,或者对比较过程进行同步控制。 通过理解并熟练运用这些方法,我们可以有效地比较和操作两个List集合,...

    C#对list列表进行随机排序的方法

    在C#编程中,列表(List)是一种常用的集合类,用于存储一组同类型的元素。当需要对列表中的元素进行随机排序时,可以采用多种方法。这里我们重点介绍一种基于`Random`类的方法,如标题和描述中所述。这种方法通过创建...

    list常用方法

    在Java编程语言中,`List`接口是集合框架的重要组成部分,它提供了有序元素集合的实现,并允许重复元素的存在。本文将详细解释`List`接口中的常用方法,帮助开发者更好地理解和运用这些功能强大的工具。 #### 1. ...

    Python如何在循环内使用list.remove()

    当在循环中使用`list.remove()`时,需要注意列表的长度会在每次调用该方法时减少1。这会导致循环的索引失效或跳过某些元素。例如: ```python dat = ['1', '2', '3', '0', '0', '0'] for item in dat: if item == ...

    Java List集合的应用.rar

    在实际编码中,我们还应注意异常处理、集合操作的效率优化以及在多线程环境下对List的同步控制。此外,List接口提供的其他方法,如`list.indexOf()`, `list.subList()`, `list.sort()`, `list.containsAll()`等,都...

    C# Array和ArrayList,List区别

    ### C# 中 Array、ArrayList 和 List 的区别 在C#编程语言中,处理集合数据时,程序员经常需要根据实际需求选择合适的集合类型。本文将详细解释C#中Array、ArrayList和List之间的区别,并通过示例代码帮助理解这些...

    list实现的购物车

    Python提供了remove()方法用于从list中删除指定元素,但需要注意处理可能的“商品不存在”异常。 4. **商品的清空**:清空购物车就是将整个list置为空。这可以通过重新赋值实现,如`cart = []`。 5. **cookie技术*...

Global site tag (gtag.js) - Google Analytics