• 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来弥补它地补足了。

  • 相关阅读:
    HTML <input> 标签
    HTML5 <input> type 属性
    静态页面与动态页面
    string::size_type 页73 size_t 页90
    template method(模板方法)
    C++中创建对象的时候加括号和不加括号的区别(转)
    _declspec(dllexport)和.def(转)
    智能指针
    C++中的delete加深认识
    工厂方法(整理自李建忠<C++设计模式>视频)
  • 原文地址:https://www.cnblogs.com/zhuangwei1015/p/10010033.html
Copyright © 2020-2023  润新知