• 集合之删除


     在阿里巴巴java开发规范中关于集合删除有一项:

    11.【强制】不要在 foreach 循环里进行元素的 remove/add 操作。remove 元素请使用
    Iterator 方式,如果并发操作,需要对 Iterator 对象加锁。
    正例:
    List<String> list = new ArrayList<>(); list.add("1");
    list.add("2");
    Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) {
    String item = iterator.next(); if (删除元素的条件) {
    12/44
    Java 开发手册
     iterator.remove(); }
    }
    反例:
    for (String item : list) {
    if ("1".equals(item)) {
    list.remove(item); }
    } 说明:以上代码的执行结果肯定会出乎大家的意料,那么试一下把“1”换成“2”,会是同样的结果吗?

    我们可以debug看一下源码:

    public class ArrayList<E> extends AbstractList<E>
            implements List<E>, RandomAccess, Cloneable, java.io.Serializable
    {
        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 remove method that skips bounds checking and does not
         * return the value removed.
         */
        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
        }
    private class Itr implements Iterator<E> {
    // 游标,下个元素的下标
    int cursor; // index of next element to return
    // 上个元素的下标
    int lastRet = -1; // index of last element returned; -1 if no such
    // 期望修改的次数
    int expectedModCount = modCount;

    Itr() {}

    /*
    是否有下一个元素
    */
    public boolean hasNext() {
    return cursor != size;
    }

    /**
    * 获取下一个元素
    * @return
    */
    @SuppressWarnings("unchecked")
    public E next() {
    // 检查改变
    checkForComodification();
    int i = cursor;
    if (i >= size)
    throw new NoSuchElementException();
    Object[] elementData = ArrayList.this.elementData;
    if (i >= elementData.length)
    throw new ConcurrentModificationException();
    cursor = i + 1;
    return (E) elementData[lastRet = i];
    }

    /**
    * 删除
    */
    public void remove() {
    if (lastRet < 0)
    throw new IllegalStateException();
    checkForComodification();

    try {
    ArrayList.this.remove(lastRet);
    cursor = lastRet;
    lastRet = -1;
    expectedModCount = modCount;
    } catch (IndexOutOfBoundsException ex) {
    throw new ConcurrentModificationException();
    }
    }


    /*
    检查改变,判断modCount与expectedModCount是否相等
    */
    final void checkForComodification() {
    if (modCount != expectedModCount)
    throw new ConcurrentModificationException();
    }
    }
     }

     上面是主要涉及到的代码,当使用for循环遍历时,其实是使用Itr去遍历。

       Itr通过游标进行遍历,遍历前先调用hasNext()方法判断是否有下一个元素,有则调用next()方法将cursor置为下一个下标。

       原示例中删除是调用的ArrayList的remove()方法,删除的时候只是将mod+1。

       所以当执行到next()方法中checkForComodification()方法时mod与expectedModCount不相等抛异常:

    Exception in thread "main" java.util.ConcurrentModificationException
        at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
        at java.util.ArrayList$Itr.next(ArrayList.java:859)
        at com.yannic.road.Demo1.main(Demo1.java:12)

      那为什么要去执行这个checkForComodification()方法呢?因为ArrayList是线程不安全的,对ArrayList一边遍历一边删除,那么遍历的结果极有可能错误的,所以java有fast-fail机制。当mod与expectedModCount不相等时,说明正在执行删除或添加操作,既然结果最终是错的,不如早点报错抛异常。这是为了多线程设置的一种机制。

          使用iterator进行遍历删除:

            Iterator iterator = list.iterator();
            while (iterator.hasNext()) {
                if (iterator.next().equals("2")) {
                    iterator.remove();
                }
            }

        这里Iterator执行remove()方法是调用的Itr的remove(),再调用完ArrayList的remove()方法会将游标置为上一个下标,并且将expectedModCount = modCount,所以再执行这个checkForComodification()方法就不会抛异常了。

  • 相关阅读:
    使用Kuboard界面在k8s上部署SpringCloud项目
    改造项目使用的Dockerfile文件
    在Kuboard上安装 Ingress Controller
    解决nexus仓库只能拉取不能推送的问题
    Logstash:运用 memcache 过滤器进行大规模的数据丰富
    Docker Compose配置文件详解(V3)
    Dockerfile 和 docker-compose.yml的区别
    实战---在Portainer中编排docker-compose.yml文件
    ctk-获取MANIFEST.MF中的数据
    继承时的析构函数
  • 原文地址:https://www.cnblogs.com/ITyannic/p/12507775.html
Copyright © 2020-2023  润新知