zoukankan      html  css  js  c++  java
  • ArrayList循环遍历并删除元素的几种情况

    如下代码,想要循环删除列表中的元素b,该怎么处理?

    public class ListDemo {
        public static void main(String[] args) {
            ArrayList<String> arrayList = new ArrayList<String>();
            arrayList.add("a");
            arrayList.add("b");
            arrayList.add("b");
            arrayList.add("c");
            arrayList.add("d");
            arrayList.add("e");
            remove(arrayList);
    //        remove2(arrayList);
    //        remove3(arrayList);
            System.out.println(arrayList);
        }
    }

    方法一:for循环遍历

           public static void remove(ArrayList<String> list) {
                for (int i = 0; i < list.size(); i++) {
                    String s = list.get(i);
                    if (s.equals("b")) {
                        list.remove(s);
                    }
                }
            }

    输出结果:

    [a, b, c, d, e]

    由结果可知,第二个元素b并未删除,原因是当第一个元素b被删除后,它后面所有的元素都向前移动了一个单位,循环时导致第二个元素b漏掉了(本例中从下标2变为了下标1,而下标1已经遍历过了),可以通过源码来看:

        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;
        }

    进入 fastRemove方法:

        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; // clear to let GC do its work }

    方法二:foreach循环

            public static void remove2(ArrayList<String> list) {
                for (String s : list) {
                    if (s.equals("b")) {
                        list.remove(s);
                    }
                    System.out.println(s);
                }
            }

    会报错:java.util.ConcurrentModificationException。这是因为在这里,foreach循环遍历容器本质上是使用迭代器进行遍历的,会对修改次数modCount进行检查,不允许集合进行更改操作,源码如下:

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

    方法三:使用迭代器遍历删除

            public static void remove3(ArrayList<String> list) {
                Iterator<String> it = list.iterator();  
                while (it.hasNext()) {  
                    String s = it.next();  
                    if (s.equals("b")) {  
                        it.remove();  
                    }  
                }  
            }

    使用迭代器遍历删除时,能够避免方法二中出现的问题。这是因为:在ArrayList中,modCount是指集合的修改次数,当进行add或者delete时,modCount会+1;expectedModCount是指集合的迭代器的版本号,初始值是modCount,但是当集合进行add或者delete操作时,modCount会+1,而expectedModCount不会改变,所以方法二中会抛出异常。但是it.remove操作时,会同步expectedModCount的值,把modCount的值赋予expectedModCount。所以不会抛出异常。it.remove的源码如下:

            public void remove() {
                if (lastRet < 0)
                    throw new IllegalStateException();
                checkForComodification();
    
                try {
                    ArrayList.this.remove(lastRet);  // 移除元素
                    cursor = lastRet;
                    lastRet = -1;
                    expectedModCount = modCount;    // 同步expectedModCount的值
                } catch (IndexOutOfBoundsException ex) {
                    throw new ConcurrentModificationException();
                }
            }

    总结:如果想正确的循环遍历删除元素,需要使用方法三,也就是迭代器遍历删除的方法。

  • 相关阅读:
    对象参数dojo异步编程之dojo/promise/all模块(dojo/DeferredList替代者)
    文件进程linux系统编程之文件与I/O(五):打开文件的内核结构file和重定向
    代码配置spring scala
    返回行javascript比较时间大小
    项目邮件[置顶] 失业的程序员(十二):潜意识的智商
    地址变形Uva 11401 Triangle Counting
    节点拓扑应用拓扑排序来解决DAG(directed acylic graph)的单源最短路径问题
    TortoiseGit使用入门
    RGMII
    ARM处理器系统初始化编程注意事项
  • 原文地址:https://www.cnblogs.com/51life/p/10406662.html
Copyright © 2011-2022 走看看