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

相关推荐

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

    注意:@{list}是robot提供的语法,python并没有@{},只有${},所以要⽐较两个@{list},需要把@{list},直接写成英⽂dollar ${list}进 ⾏对⽐ ⽰例: @{list} Create List lilei hanmeimei liming liliang liming ...

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

    主要介绍了Java list.remove( )方法注意事项,非常简单易懂,需要的朋友可以参考下

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

    主要介绍了Python列表list内建函数用法,结合具体实例形式分析了list中insert、remove、index、pop等函数的功能、使用方法与相关注意事项,需要的朋友可以参考下

    Python中remove漏删和索引越界问题

    list.remove方法在删除元素的时候往往会出现漏删或者索引越界的情况示例如下: 漏删: lst=[9,25,12,36] for i in lst: if i&gt;10: lst.remove(i) print(lst) &gt;&gt;&gt;[9, 12] 那么为什么12被漏删了呢?其实原理很简单...

    Python中remove漏删和索引越界问题的解决

    list.remove方法在删除元素的时候往往会出现漏删或者索引越界的情况示例如下: 漏删: lst=[9,25,12,36] for i in lst: if i&gt;10: lst.remove(i) print(lst) &gt;&gt;&gt;[9, 12] 那么为什么12被漏删了呢?其实原理很简单...

    python list删除元素时要注意的坑点分享

    在上述for循环中,假设我们删除了index=2的值,原本index=3及之后的值会向前补位,所以在循环中就跳过了原index=3的变量 同理,使用list.pop()函数删除指定元素的时候,也会出现上述情况,如: In[33]: a = [1,2,3,...

    Java中的List集合

    1 Java中的List集合 1.1 迭代器 通过集合对象获取对应的Iterator迭代器  Iterator iterator(); 常用方法:  boolean hasNext();  判断当前Iterator是否可以继续运行。  E next();  获取Iterator当前指向元素,...

    0到9生成4个8位数的字符串.docx

    //题目:0到9这几个数字生成4个不相同的8位数的字符串,例如12345678,12345687,12345786,... //在List中将这个数remove掉,那么下次从集合中获取一个数字的时候就不会重复了,只是这种方式贼鸡儿麻烦。。。我也是醉了

    beiyou Java作业 -1

    注意:为了保证calculateAverage返回double类型,需要把三个分数的和除以3.0,而不是3. 另外,分数的初始值是什么?如果每个分数初始值为0,会造成混淆,分数为0表示还没有输入分数,还是分数确实为0?有更好的初始...

    大家注意vector, list, set, map成员函数erase

    代码如下:#include &lt;iostream&gt;#include &lt;vector&gt;#include &lt;list&gt;#include &lt;iterator&gt;using namespace std; void Remove1(vector&lt;int&gt; &vec, int num){ vector&lt;int&gt;::...void Remove2(list&lt;int&gt; &lst, int num){

    AngularJS实践之使用ng-repeat中$index的注意点

    要定位这个BUG非常麻烦, 因为客户也不清楚如何重现这个问题。 后来发现这个Bug是由于在 ng-repeat 中使用了 $index 引发的。 一个简单动作(action)的列表 先来看看一个完整有效的ng-repeat示例。 ...

    plsqldev13.0.1.1893x64主程序+ v12中文包+keygen

    64位版本的 PLSQL 正式版,只能运行在64位系统中,需要你安装 64 位的 Oracle11g 或 Oracle12c 客户端。 安装中文包时请注意安装路径是否为PLSQL程序的路径。 安装请查看说明。 PL/SQL Developer 13.0 - New ...

    perl-calendar:基于 Google Calendar API 的 Perl 中基于控制台的日历

    perl-日历 基于 Google Calendar ...要编辑现有的事件运行:(注意:如果您运行列表,程序将要求一个事件编号,该编号是每个事件顶部的编号。) edit_event 要删除事件运行: remove_event 要退出程序运行: exit

    leetcode括号生成python-leetcode:leetcode

    leetcode括号生成python leetcode 打算开始在leetcode上刷题了嗯。 希望不要因为各种各样的原因半途而废…… 2015.3.28 在这里贴一下写过的题解和思路。 长期更新。...唯一要注意的地方是 n 等于链表长度的

    JDKAPI18CN(中文版)

    The iterators returned by this class's个 iterator和listIterator方法是快速失败的 :如果列表在任何时间从结构上修改创建迭代器之后,以任何方式除非通过迭代器自身remove种或add方法,迭代器都将抛出一个...

    plsqldev13.0.1.1893x32主程序+ v12中文包+keygen

    安装中文包时请注意安装路径是否为PLSQL程序的路径。 安装请查看说明。 PL/SQL Developer 13.0 - New Features Enhancements Language packs added for Brazilian, Chinese, French, German, Hungarian, Japanese...

    生产电工实习(电子技术实习报告)

    (3)对照设计原理图,依次从library list中找到图上所需要的元器件,先依照数目全部添加,待所有元器件均在边框之内时,再照原理图上各元器件位置将元器件翻转,旋转,放置在合理位置(移动元件时只需将鼠标放在...

    计算机辅助设计课堂笔记.docx

    几何特征的简化(去孔用defuture中pinholes点find(注意容差)删除面于面之间的圆角用defuture中的surfs fillets选择时左击为选中,右键为取消,中键为remove(确认,在个命 令中不同)去除边的圆角用的future中的...

    ArrayList.java

    除了实现List接口,此类提供方法来操作在内部用于存储列表中的阵列的大小。 (这个类是大致相当于矢量 ,除了它是不同步的)。 大小 的isEmpty, 获取 , 设置 迭代器和操作的ListIterator在固定时间内运行。 在...

    北邮java作业

    注意:为了保证calculateAverage返回double类型,需要把三个分数的和除以3.0,而不是3. 另外,分数的初始值是什么?如果每个分数初始值为0,会造成混淆,分数为0表示还没有输入分数,还是分数确实为0?有更好的初始...

Global site tag (gtag.js) - Google Analytics