zoukankan      html  css  js  c++  java
  • 容器List之ArrayList详解

    什么是ArrayList?

    ArrayList是Java集合常用的数据结构之一,继承自AbstractList,实现了List,RandomAccess、Cloneable、Serializable等一系列接口,支持快速访问,复制和序列化。底层是基于数组实现容量大小动态变化,允许null值存在。
    默认size的初始大小为10:
    扩容公式为
    当前容量长度*1.5
    即:默认长度为10
    第一次扩充后的长度为15
    第二次扩充后的长度为22
    第三次扩从后的长度为33

    源码解析

    源代码解析原博客在这里更详细请点击!

    类属性

    	 /**
         * 序列号
         */
        private static final long serialVersionUID = 8683452581122892189L;
        /**
         * 默认容量
         */
        private static final int DEFAULT_CAPACITY = 10;
        /**
         * 一个空数组
         * 当用户指定该 ArrayList 容量为 0 时,返回该空数组
         */
        private static final Object[] EMPTY_ELEMENTDATA = {};
        
        /**
         * 一个空数组实例
         * - 当用户调用无参构造函数,返回的是该数组==>刚创建一个 ArrayList 时,其内数据量为 0。
         * - 当用户第一次添加元素时,该数组将会扩容,变成默认容量为 10(DEFAULT_CAPACITY) 的一个数组===>通过  ensureCapacityInternal() 实现
         * 它与 EMPTY_ELEMENTDATA 的区别就是:该数组是默认返回的,而后者是在用户指定容量为 0 时返回
         */
        private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
         /**
         * ArrayList基于数组实现,用该数组保存数据, ArrayList 的容量就是该数组的长度
         * - 该值为 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 时,当第一次添加元素进入 ArrayList 中时,数组将扩容值 DEFAULT_CAPACITY(10)
         */
        transient Object[] elementData; 
        /**
         * ArrayList实际存储的数据数量
         */
        private int size;    
    

    构造方法:

    /**
         * 创建一个具有初试容量的ArrayList
         * @param  initialCapacity  初始容量
         * @throws IllegalArgumentException 当初试容量值非法(小于0)时抛出
         */
    public ArrayList(int initialCapacity) {
            if (initialCapacity > 0) {
                this.elementData = new Object[initialCapacity];
            } else if (initialCapacity == 0) {
                this.elementData = EMPTY_ELEMENTDATA;
            } else {
                throw new IllegalArgumentException("Illegal Capacity: "+
                                                   initialCapacity);
            }
        }
    	/**
         * 无参构造函数:
         * - 创建一个 空的 ArrayList,此时其内数组缓冲区 elementData = {}, 长度为 0
         * - 当元素第一次被加入时,扩容至默认容量 10
         */
        public ArrayList() {
            this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
        }
        /**
         * 创建一个包含collection的ArrayList
         * @param c 要放入 ArrayList 中的集合,其内元素将会全部添加到新建的 ArrayList 实例中
         * @throws NullPointerException 当参数 c 为 null 时抛出异常
         */
        public ArrayList(Collection<? extends E> c) {
            //将集合转化成Object[]数组
            elementData = c.toArray();
            //把转化后的Object[]数组长度赋值给当前ArrayList的size,并判断是否为0
            if ((size = elementData.length) != 0) {
                // c.toArray might (incorrectly) not return Object[] (see 6260652)
                // 这句话意思是:c.toArray 可能不会返回 Object[],可以查看 java 官方编号为 6260652 的 bug
                if (elementData.getClass() != Object[].class)
                    // 若 c.toArray() 返回的数组类型不是 Object[],则利用 Arrays.copyOf(); 来构造一个大小为 size 的 Object[] 数组
                    elementData = Arrays.copyOf(elementData, size, Object[].class);
            } else {
                // 替换空数组
                this.elementData = EMPTY_ELEMENTDATA;
            }
        }
    

    类方法

     	/**
         * 将数组缓冲区大小调整到实际 ArrayList 存储元素的大小,即 elementData = Arrays.copyOf(elementData, size);
         * - 该方法由用户手动调用,以减少空间资源浪费的目的 ce.
         */
        public void trimToSize() {
            // modCount 是 AbstractList 的属性值:protected transient int modCount = 0;
            // [问] modCount 有什么用?
            modCount++;
            // 当实际大小 < 数组缓冲区大小时
            // 如调用默认构造函数后,刚添加一个元素,此时 elementData.length = 10,而 size = 1
            // 通过这一步,可以使得空间得到有效利用,而不会出现资源浪费的情况
            if (size < elementData.length) {
                // 注意这里:这里的执行顺序不是 (elementData = (size == 0) ) ? EMPTY_ELEMENTDATA : Arrays.copyOf(elementData, size);
                // 而是:elementData = ((size == 0) ? EMPTY_ELEMENTDATA : Arrays.copyOf(elementData, size));
                // 这里是运算符优先级的语法
                // 调整数组缓冲区 elementData,变为实际存储大小 Arrays.copyOf(elementData, size)
                //先判断size是否为0,如果为0:实际存储为EMPTY_ELEMENTDATA,如果有数据就是Arrays.copyOf(elementData, size)
                elementData = (size == 0)
                        ? EMPTY_ELEMENTDATA
                        : Arrays.copyOf(elementData, size);
            }
        }
    
    
    	/**
         * 指定 ArrayList 的容量
         * @param   minCapacity   指定的最小容量
         */
        public void ensureCapacity(int minCapacity) {
            // 最小扩充容量,默认是 10
            //这句就是:判断是不是空的ArrayList,如果是的最小扩充容量10,否则最小扩充量为0
            //上面无参构造函数创建后,当元素第一次被加入时,扩容至默认容量 10,就是靠这句代码
            int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
                    ? 0
                    : DEFAULT_CAPACITY;
            // 若用户指定的最小容量 > 最小扩充容量,则以用户指定的为准,否则还是 10
            if (minCapacity > minExpand) {
                ensureExplicitCapacity(minCapacity);
            }
        }
        
     	/**
         * 私有方法:明确 ArrayList 的容量,提供给本类使用的方法
         * - 用于内部优化,保证空间资源不被浪费:尤其在 add() 方法添加时起效
         * @param minCapacity    指定的最小容量
         */
        private void ensureCapacityInternal(int minCapacity) {
            // 若 elementData == {},则取 minCapacity 为 默认容量和参数 minCapacity 之间的最大值
            // 注:ensureCapacity() 是提供给用户使用的方法,在 ArrayList 的实现中并没有使用
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
                minCapacity= Math.max(DEFAULT_CAPACITY, minCapacity);
            }
            ensureExplicitCapacity(minCapacity);
        }
     	/**
         * 私有方法:明确 ArrayList 的容量
         * - 用于内部优化,保证空间资源不被浪费:尤其在 add() 方法添加时起效
         * @param minCapacity    指定的最小容量
         */
        private void ensureExplicitCapacity(int minCapacity) {
            // 将“修改统计数”+1,该变量主要是用来实现fail-fast机制的
            modCount++;
            // 防止溢出代码:确保指定的最小容量 > 数组缓冲区当前的长度
            // overflow-conscious code
            if (minCapacity - elementData.length > 0)
                grow(minCapacity);
        }
    
    
    	/**
         * 数组缓冲区最大存储容量
         * - 一些 VM 会在一个数组中存储某些数据--->为什么要减去 8 的原因
         * - 尝试分配这个最大存储容量,可能会导致 OutOfMemoryError(当该值 > VM 的限制时)
         */
        private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    
        /**
         * 私有方法:扩容,以确保 ArrayList 至少能存储 minCapacity 个元素
         * - 扩容计算:newCapacity = oldCapacity + (oldCapacity >> 1);  扩充当前容量的1.5倍
         * @param minCapacity    指定的最小容量
         */
        private void grow(int minCapacity) {
            // 防止溢出代码
            int oldCapacity = elementData.length;
            // 运算符 >> 是带符号右移. 如 oldCapacity = 10,则 newCapacity = 10 + (10 >> 1) = 10 + 5 = 15
            int newCapacity = oldCapacity + (oldCapacity >> 1);
            if (newCapacity - minCapacity < 0)  // 若 newCapacity 依旧小于 minCapacity
                newCapacity = minCapacity;
            if (newCapacity - MAX_ARRAY_SIZE > 0)   // 若 newCapacity 大于最大存储容量,则进行大容量分配
                newCapacity = hugeCapacity(minCapacity);
            // minCapacity is usually close to size, so this is a win:
            elementData = Arrays.copyOf(elementData, newCapacity);
        }
        /**
         * 私有方法:大容量分配,最大分配 Integer.MAX_VALUE
         * @param minCapacity
         */
        private static int hugeCapacity(int minCapacity) {
            if (minCapacity < 0) // overflow
                throw new OutOfMemoryError();
            return (minCapacity > MAX_ARRAY_SIZE) ?
                    Integer.MAX_VALUE :
                    MAX_ARRAY_SIZE;
        }
    
    	/**
         * 返回ArrayList实际存储的元素数量
         */
        public int size() {
            return size;
        }
    
    	/**
         * ArrayList是否有元素
         */
        public boolean isEmpty() {
            return size == 0;
        }
    	/**
         * 是否包含o元素
         */
        public boolean contains(Object o) {
            // 根据 indexOf() 的值(索引值)来判断,大于等于 0 就包含
            // 注意:等于 0 的情况不能漏,因为索引号是从 0 开始计数的
            return indexOf(o) >= 0;
        }
    	
    	 /**
         * 顺序查找,返回元素的最低索引值(最首先出现的索引位置)
         * @return 存在?最低索引值:-1
         */
        public int indexOf(Object o) {
            if (o == null) {
                for (int i = 0; i < size; i++)
                    if (elementData[i]==null)
                        return i;
            } else {
                for (int i = 0; i < size; i++)
                    if (o.equals(elementData[i]))
                        return i;
            }
            return -1;
        }
        	
        /**
         * 逆序查找,返回元素的最低索引值(最首先出现的索引位置)
         * @return 存在?最低索引值:-1
         */
        public int lastIndexOf(Object o) {
            if (o == null) {
                for (int i = size-1; i >= 0; i--)
                    if (elementData[i]==null)
                        return i;
            } else {
                for (int i = size-1; i >= 0; i--)
                    if (o.equals(elementData[i]))
                        return i;
            }
            return -1;
        }
    	
    	/**
         * 返回 ArrayList 的 Object 数组
         * - 包含 ArrayList 的所有储存元素
         * - 对返回的该数组进行操作,不会影响该 ArrayList(相当于分配了一个新的数组)==>该操作是安全的
         * - 元素存储顺序与 ArrayList 中的一致
         */
        public Object[] toArray() {
            return Arrays.copyOf(elementData, size);
        }
    
     	/**
         * 返回 ArrayList 元素组成的数组
         * @param a 需要存储 list 中元素的数组
         * 若 a.length >= list.size,则将 list 中的元素按顺序存入 a 中,然后 a[list.size] = null, a[list.size + 1] 及其后的元素依旧是 a 的元素
         * 否则,将返回包含list 所有元素且数组长度等于 list 中元素个数的数组
         * 注意:若 a 中本来存储有元素,则 a 会被 list 的元素覆盖,且 a[list.size] = null
         * @return
         * @throws ArrayStoreException 当 a.getClass() != list 中存储元素的类型时
         * @throws NullPointerException 当 a 为 null 时
         */
        @SuppressWarnings("unchecked")
        public <T> T[] toArray(T[] a) {
            // 若数组a的大小 < ArrayList的元素个数,则新建一个T[]数组,
            // 数组大小是"ArrayList的元素个数",并将“ArrayList”全部拷贝到新数组中
            if (a.length < size)
                // Make a new array of a's runtime type, but my contents:
                return (T[]) Arrays.copyOf(elementData, size, a.getClass());
            // 若数组a的大小 >= ArrayList的元素个数,则将ArrayList的全部元素都拷贝到数组a中。
            System.arraycopy(elementData, 0, a, 0, size);
            if (a.length > size)
                a[size] = null;
            return a;
        }
    
    
     	/**
         * 获取指定位置上的元素,从0开始
         */
        public E get(int index) {
            rangeCheck(index);//检查是否越界
            return elementData(index);
        }
        
    	/**
         * 设置 index 位置元素的值
         * @param index 索引值
         * @param element 需要存储在 index 位置的元素值
         * @return 替换前在 index 位置的元素值
         * @throws IndexOutOfBoundsException {@inheritDoc}
         */
        public E set(int index, E element) {
            rangeCheck(index);//越界检查
    
            E oldValue = elementData(index);//获取旧数值
            elementData[index] = element;
            return oldValue;
        }
    	/**
         *增加指定的元素到ArrayList的最后位置
         * @param e 要添加的元素
         * @return
         */
        public boolean add(E e) {
            // 确定ArrayList的容量大小---严谨
            // 注意:size + 1,保证资源空间不被浪费,
            // ☆☆☆按当前情况,保证要存多少个元素,就只分配多少空间资源
            ensureCapacityInternal(size + 1);  // Increments modCount!!
            elementData[size++] = e;
            return true;
        }
    
    	/**
         *
         *在这个ArrayList中的指定位置插入指定的元素,
         *  - 在指定位置插入新元素,原先在 index 位置的值往后移动一位
         * @param index 指定位置
         * @param element 指定元素
         * @throws IndexOutOfBoundsException
         */
        public void add(int index, E element) {
            rangeCheckForAdd(index);//判断角标是否越界
            //看上面的,size+1,保证资源空间不浪费,按当前情况,保证要存多少元素,就只分配多少空间资源
            ensureCapacityInternal(size + 1);  // Increments modCount!!
            //第一个是要复制的数组,第二个是从要复制的数组的第几个开始,
            // 第三个是复制到那,四个是复制到的数组第几个开始,最后一个是复制长度
            System.arraycopy(elementData, index, elementData, index + 1,
                    size - index);
            elementData[index] = element;
            size++;
        }
    
    	/**
         * 移除指定位置的元素
         * index 之后的所有元素依次左移一位
         * @param index 指定位置
         * @return 被移除的元素
         * @throws IndexOutOfBoundsException
         */
        public E remove(int index) {
            rangeCheck(index);
    
            modCount++;
            E oldValue = elementData(index);
    
            int numMoved = size - index - 1;//要移动的长度
            if (numMoved > 0)
                System.arraycopy(elementData, index+1, elementData, index,
                        numMoved);
            // 将最后一个元素置空
            elementData[--size] = null;
    
            return oldValue;
        }
         /**
         * 移除list中指定的第一个元素(符合条件索引最低的)
         * 如果list中不包含这个元素,这个list不会改变
         * 如果包含这个元素,index 之后的所有元素依次左移一位
         * @param o 这个list中要被移除的元素
         * @return
         */
        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;
        }
    	
    	 /**
         * 快速删除第 index 个元素
         * 和public E remove(int index)相比
         * 私有方法,跳过检查,不返回被删除的值
         * @param index 要删除的脚标
         */
        private void fastRemove(int index) {
            modCount++;//这个地方改变了modCount的值了
            int numMoved = size - index - 1;//移动的个数
            if (numMoved > 0)
                System.arraycopy(elementData, index+1, elementData, index,
                        numMoved);
            elementData[--size] = null; //将最后一个元素清除
        }
    	/**
         * 移除list中的所有元素,这个list表将在调用之后置空
         * - 它会将数组缓冲区所以元素置为 null
         * - 清空后,我们直接打印 list,却只会看见一个 [], 而不是 [null, null, ….] ==> toString() 和 迭代器进行了处理
         */
        public void clear() {
            modCount++;
    
            // clear to let GC do its work
            for (int i = 0; i < size; i++)
                elementData[i] = null;
    
            size = 0;
        }
    	
    	/**
         * 将一个集合的所有元素顺序添加(追加)到 lits 末尾
         * - ArrayList 是线程不安全的。
         * - 该方法没有加锁,当一个线程正在将 c 中的元素加入 list 中,但同时有另一个线程在更改 c 中的元素,可能会有问题
         * @param c  要追加的集合
         * @return <tt>true</tt> ? list 元素个数有改变时,成功:失败
         * @throws NullPointerException 当 c 为 null 时
         */
        public boolean addAll(Collection<? extends E> c) {
            Object[] a = c.toArray();
            int numNew = a.length;//要添加元素的个数
            ensureCapacityInternal(size + numNew);  //扩容
            System.arraycopy(a, 0, elementData, size, numNew);
            size += numNew;
            return numNew != 0;
        }
    	
    	/**
         * 从 List 中指定位置开始插入指定集合的所有元素,
         * -list中原来位置的元素向后移
         * - 并不会覆盖掉在 index 位置原有的值
         * - 类似于 insert 操作,在 index 处插入 c.length 个元素(原来在此处的 n 个元素依次右移)
         * @param index 插入指定集合的索引
         * @param c 要添加的集合
         * @return ? list 元素个数有改变时,成功:失败
         * @throws IndexOutOfBoundsException {@inheritDoc}
         * @throws NullPointerException if the specified collection is null
         */
        public boolean addAll(int index, Collection<? extends E> c) {
            rangeCheckForAdd(index);
    
            Object[] a = c.toArray();//是将list直接转为Object[] 数组
            int numNew = a.length;  //要添加集合的元素数量
            ensureCapacityInternal(size + numNew);  // 扩容
    
            int numMoved = size - index;//list中要移动的数量
            if (numMoved > 0)
                System.arraycopy(elementData, index, elementData, index + numNew,
                        numMoved);
    
            System.arraycopy(a, 0, elementData, index, numNew);
            size += numNew;
            return numNew != 0;
        }
    	
    	 /**
         * 移除list中指定集合包含的所有元素
         * @param c 要从list中移除的指定集合
         * @return {@code true} if this list changed as a result of the call
         * @throws ClassCastException 如果list中的一个元素的类和指定集合不兼容
         * (<a href="Collection.html#optional-restrictions">optional</a>)
         * @throws NullPointerException  如果list中包含一个空元素,而指定集合中不允许有空元素
         */
        public boolean removeAll(Collection<?> c) {
            Objects.requireNonNull(c);//判断集合是否为空,如果为空报NullPointerException
            //批量移除c集合的元素,第二个参数:是否采补集
            return batchRemove(c, false);
        }
    
    
    	/**
         * 返回从指定索引开始到结束的带有元素的list迭代器
         */
        public ListIterator<E> listIterator(int index) {
            if (index < 0 || index > size)
                throw new IndexOutOfBoundsException("Index: "+index);
            return new ListItr(index);
        }
        /**
         * 返回从0索引开始到结束的带有元素的list迭代器
         */
        public ListIterator<E> listIterator() {
            return new ListItr(0);
        }
        
    	/**
         * 以一种合适的排序返回一个iterator到元素的结尾
         */
        public Iterator<E> iterator() {
            return new Itr();
        }
    
    
    	/**
         * Itr是AbstractList.Itr的优化版本
         * 为什么会报ConcurrentModificationException异常?
         * 1. Iterator 是工作在一个独立的线程中,并且拥有一个 mutex 锁。
         * 2. Iterator 被创建之后会建立一个指向原来对象的单链索引表,当原来的对象数量发生变化时,
         * 这个索引表的内容不会同步改变,所以当索引指针往后移动的时候就找不到要迭代的对象,
         * 3. 所以按照 fail-fast 原则 Iterator 会马上抛出 java.util.ConcurrentModificationException 异常。
         * 4. 所以 Iterator 在工作的时候是不允许被迭代的对象被改变的。
         * 但你可以使用 Iterator 本身的方法 remove() 来删除对象,
         * 5. Iterator.remove() 方法会在删除当前迭代对象的同时维护索引的一致性。
         */
        private class Itr implements Iterator<E> {
            int cursor;       // 下一个元素返回的索引
            int lastRet = -1; // 最后一个元素返回的索引  -1 if no such
            int expectedModCount = modCount;
    
            /**
             * 是否有下一个元素
             */
            public boolean hasNext() {
                return cursor != size;
            }
    
            /**
             * 返回list中的值
             */
            @SuppressWarnings("unchecked")
            public E next() {
                checkForComodification();
                int i = cursor;//i当前元素的索引
                if (i >= size)//第一次检查:角标是否越界越界
                    throw new NoSuchElementException();
                Object[] elementData = ArrayList.this.elementData;
                if (i >= elementData.length)//第二次检查,list集合中数量是否发生变化
                    throw new ConcurrentModificationException();
                cursor = i + 1; //cursor 下一个元素的索引
                return (E) elementData[lastRet = i];//最后一个元素返回的索引
            }
    
            /**
             * 移除集合中的元素
             */
            public void remove() {
                if (lastRet < 0)
                    throw new IllegalStateException();
                checkForComodification();
    
                try {
                    //移除list中的元素
                    ArrayList.this.remove(lastRet);
                    //由于cursor比lastRet大1,所有这行代码是指指针往回移动一位
                    cursor = lastRet;
                    //将最后一个元素返回的索引重置为-1
                    lastRet = -1;
                    //重新设置了expectedModCount的值,避免了ConcurrentModificationException的产生
                    expectedModCount = modCount;
                } catch (IndexOutOfBoundsException ex) {
                    throw new ConcurrentModificationException();
                }
            }
    
            /**
             * jdk 1.8中使用的方法
             * 将list中的所有元素都给了consumer,可以使用这个方法来取出元素
             */
            @Override
            @SuppressWarnings("unchecked")
            public void forEachRemaining(Consumer<? super E> consumer) {
                Objects.requireNonNull(consumer);
                final int size = ArrayList.this.size;
                int i = cursor;
                if (i >= size) {
                    return;
                }
                final Object[] elementData = ArrayList.this.elementData;
                if (i >= elementData.length) {
                    throw new ConcurrentModificationException();
                }
                while (i != size && modCount == expectedModCount) {
                    consumer.accept((E) elementData[i++]);
                }
                // update once at end of iteration to reduce heap write traffic
                cursor = i;
                lastRet = i - 1;
                checkForComodification();
            }
    
            /**
             * 检查modCount是否等于expectedModCount
             * 在 迭代时list集合的元素数量发生变化时会造成这两个值不相等
             */
            final void checkForComodification() {
                //当expectedModCount和modCount不相等时,就抛出ConcurrentModificationException
                if (modCount != expectedModCount)
                    throw new ConcurrentModificationException();
            }
        }
    
    
    
    
    
    	/**
         * AbstractList.ListItr 的优化版本
         * ListIterator 与普通的 Iterator 的区别:
         * - 它可以进行双向移动,而普通的迭代器只能单向移动
         * - 它可以添加元素(有 add() 方法),而后者不行
         */
        private class ListItr extends Itr implements ListIterator<E> {
            ListItr(int index) {
                super();
                cursor = index;
            }
    
            /**
             * 是否有前一个元素
             */
            public boolean hasPrevious() {
                return cursor != 0;
            }
    
            /**
             * 获取下一个元素的索引
             */
            public int nextIndex() {
                return cursor;
            }
    
            /**
             * 获取 cursor 前一个元素的索引
             * - 是 cursor 前一个,而不是当前元素前一个的索引。
             * - 若调用 next() 后马上调用该方法,则返回的是当前元素的索引。
             * - 若调用 next() 后想获取当前元素前一个元素的索引,需要连续调用两次该方法。
             */
            public int previousIndex() {
                return cursor - 1;
            }
    
            /**
             * 返回 cursor 前一元素
             */
            @SuppressWarnings("unchecked")
            public E previous() {
                checkForComodification();
                int i = cursor - 1;
                if (i < 0)//第一次检查:索引是否越界
                    throw new NoSuchElementException();
                Object[] elementData = ArrayList.this.elementData;
                if (i >= elementData.length)//第二次检查
                    throw new ConcurrentModificationException();
                cursor = i;//cursor回移
                return (E) elementData[lastRet = i];//返回 cursor 前一元素
            }
    
            /**
             * 将数组的最后一个元素,设置成元素e
             */
            public void set(E e) {
                if (lastRet < 0)
                    throw new IllegalStateException();
                checkForComodification();
    
                try {
                    //将数组最后一个元素,设置成元素e
                    ArrayList.this.set(lastRet, e);
                } catch (IndexOutOfBoundsException ex) {
                    throw new ConcurrentModificationException();
                }
            }
    
            /**
             * 添加元素
             */
            public void add(E e) {
                checkForComodification();
    
                try {
                    int i = cursor;//当前元素的索引后移一位
                    ArrayList.this.add(i, e);//在i位置上添加元素e
                    cursor = i + 1;//cursor后移一位
                    lastRet = -1;
                    expectedModCount = modCount;
                } catch (IndexOutOfBoundsException ex) {
                    throw new ConcurrentModificationException();
                }
            }
        }
    
  • 相关阅读:
    CODING x 百果园 _ 水果零售龙头迈出 DevOps 体系建设第一步
    Nocalhost 亮相 CD Foundation 国内首届 Meetup,Keith Chan 将出席致辞
    做云原生时代标准化工具,实现高效云上研发工作流
    打造数字化软件工厂 —— 一站式 DevOps 平台全景解读
    WePack —— 助力企业渐进式 DevOps 转型
    CODING Compass —— 打造行云流水般的软件工厂
    Nocalhost —— 让云原生开发回归原始而又简单
    CODING 代码资产安全系列之 —— 构建全链路安全能力,守护代码资产安全
    Nocalhost:云原生开发新体验
    使用 Nocalhost 开发 Kubernetes 中的 APISIX Ingress Controller
  • 原文地址:https://www.cnblogs.com/luckyhui28/p/11235891.html
Copyright © 2011-2022 走看看