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

    四、获取元素

      一个我们常见的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;
        }
    View Code

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

      

      //元素置Null与size置0

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

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

  • 相关阅读:
    ZOJ 3705 Applications
    UVA 220 Othello
    HDU 2084 数塔
    第五章:变量
    第四章:注释
    第三章:程序结构
    第二章:项目的创建和环境熟悉
    第一章:c#开发环境安装
    处理字段串
    查询表的列信息
  • 原文地址:https://www.cnblogs.com/jiangbei/p/7463621.html
Copyright © 2011-2022 走看看