zoukankan      html  css  js  c++  java
  • Java基础—ArrayList源码浅析

    注:以下源码均为JDK8的源码

    一、 核心属性

      基本属性如下:

      

      核心的属性其实是红框中的两个:

      

      //从注释也容易看出,一个是集合元素,一个是集合长度(注意是逻辑长度,即元素的个数,而非数组长度)

      其中:transient指明序列化时请忽略。

     二、构造器

       一共有3个构造器:

      

        1.构造指定容量的ArrayList

        

        2.默认构造器

        

        //可以看到,默认初始容量为10(基本属性中的DEFAULT_CAPACITY

        而 DEFAULTCAPACITY_EMPTY_ELEMENTDATA是一个空数组,所有默认构造器初始化的都指向它,目的是为了防止我们创建过多的无用的List

        如果创建ArrayList时用的是无参构造器,则第一次插入时会进行一次扩容并且扩到默认数组大小10

        3.使用collection参数的构造器

        

        //接收一个集合进行构造,按照迭代器返回的顺序排列

      更多容器初始化的介绍,参考是清浅池塘知乎专栏:https://zhuanlan.zhihu.com/p/27873515

    三、添加元素

      一共有5个(包含2个重载方法)

      

       //由于是基于数组进行实现的,我们只要抓住它的特点,就能读懂总体的思路

       1.set(int,E),使用新元素,替代指定位置旧元素

        

        //比较容易读懂的一个方法:检查下标->取得旧元素->替代->返回新元素

        2.add(E)/add(int,E),包含两个重载方法:在末尾添加元素与在指定位置添加元素

       

       //先检查容量是否需要扩容(扩容算法不在这里展开),再在结尾添加元素(size+1)

       

        //同样的,涉及下标的都先检查下标,之后检查是否需要扩容,之后,由于是数组,先右移Index+1的元素,再添加

        3.addAll(),两个addAll方法,与上面类似,一个为在末尾添加,一个在指定位置添加,顺序均为迭代器返回的顺序,这里就不展开:

    /**
         * Appends all of the elements in the specified collection to the end of
         * this list, in the order that they are returned by the
         * specified collection's Iterator.  The behavior of this operation is
         * undefined if the specified collection is modified while the operation
         * is in progress.  (This implies that the behavior of this call is
         * undefined if the specified collection is this list, and this
         * list is nonempty.)
         *
         * @param c collection containing elements to be added to this list
         * @return <tt>true</tt> if this list changed as a result of the call
         * @throws NullPointerException if the specified collection is null
         */
        public boolean addAll(Collection<? extends E> c) {
            Object[] a = c.toArray();
            int numNew = a.length;
            ensureCapacityInternal(size + numNew);  // Increments modCount
            System.arraycopy(a, 0, elementData, size, numNew);
            size += numNew;
            return numNew != 0;
        }
    
        /**
         * Inserts all of the elements in the specified collection into this
         * list, starting at the specified position.  Shifts the element
         * currently at that position (if any) and any subsequent elements to
         * the right (increases their indices).  The new elements will appear
         * in the list in the order that they are returned by the
         * specified collection's iterator.
         *
         * @param index index at which to insert the first element from the
         *              specified collection
         * @param c collection containing elements to be added to this list
         * @return <tt>true</tt> if this list changed as a result of the call
         * @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();
            int numNew = a.length;
            ensureCapacityInternal(size + numNew);  // Increments modCount
    
            int numMoved = size - index;
            if (numMoved > 0)
                System.arraycopy(elementData, index, elementData, index + numNew,
                                 numMoved);
    
            System.arraycopy(a, 0, elementData, index, numNew);
            size += numNew;
            return numNew != 0;
        }

    四、获取元素

      一个我们常见的get方法:

      

      //比较简单的下标检查,获取元素

    五、删除元素

      3个基本的删除方法:

      

      1.remove(int)/remove(Object),两个重载方法,分别是按照下标和按照元素删除:

      

      

      //范围检查->修改次数modCount加1->得到将要删除的元素,被删除元素后的元素向前进一位,末尾元素置空。

      关于modCount:

     ArrayList也采用了快速失败的机制,通过记录modCount参数来实现。在面对并发的修改时,迭代器很快就会完全失败,而不是冒着在将来某个不确定时间发生任意不确定行为的风险。 

    在使用迭代器遍历的时候,如果使用ArrayList中的remove(int index) remove(Object o) remove(int fromIndex ,int toIndex) add等方法的时候都会修改modCount,在迭代的时候需要保持单线程的唯一操作,如果期间进行了插入或者删除,就会被迭代器检查获知,从而出现运行时异常

      

      

      //先检查要删除的元素在不在,存在则删除返回true,不存在返回false

      2.removeRange(int,int),按照范围删除,实际是将elementData从toIndex位置开始的元素向前移动到fromIndex,然后将toIndex位置之后的元素全部置空顺便修改size。这里就不展开频率使用不高的范围删除方法了:

    /**
         * Removes from this list all of the elements whose index is between
         * {@code fromIndex}, inclusive, and {@code toIndex}, exclusive.
         * Shifts any succeeding elements to the left (reduces their index).
         * This call shortens the list by {@code (toIndex - fromIndex)} elements.
         * (If {@code toIndex==fromIndex}, this operation has no effect.)
         *
         * @throws IndexOutOfBoundsException if {@code fromIndex} or
         *         {@code toIndex} is out of range
         *         ({@code fromIndex < 0 ||
         *          fromIndex >= size() ||
         *          toIndex > size() ||
         *          toIndex < fromIndex})
         */
        protected void removeRange(int fromIndex, int toIndex) {
            modCount++;
            int numMoved = size - toIndex;
            System.arraycopy(elementData, toIndex, elementData, fromIndex,
                             numMoved);
    
            // clear to let GC do its work
            int newSize = size - (toIndex-fromIndex);
            for (int i = newSize; i < size; i++) {
                elementData[i] = null;
            }
            size = newSize;
        }

    清空元素也比较容易看懂:

      

      //元素置Null与size置0

      在ArrayList中,底层数组存/取元素效率非常的高(get/set),时间复杂度是O(1),而查找,插入和删除元素效率不高,时间复杂度为O(n)。

    并且由源码也知道,频繁地插入删除会导致底层数组地复制,这个应当由 LinkedList来弥补它地补足了。

  • 相关阅读:
    三数之和
    罗马数字与整数
    Oracle 开启或关闭归档
    Oracle RMAN scripts to delete archivelog
    Oracle check TBS usage
    Oracle kill locked sessions
    场景9 深入RAC运行原理
    场景7 Data Guard
    场景4 Data Warehouse Management 数据仓库
    场景5 Performance Management
  • 原文地址:https://www.cnblogs.com/zhuangwei1015/p/10010033.html
Copyright © 2011-2022 走看看