zoukankan      html  css  js  c++  java
  • java集合之ArrayList的实现原理

    1. ArrayList概述:

       ArrayList是List接口的可变数组的实现。实现了所有可选列表操作,并允许包括 null 在内的所有元素。除了实现 List 接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小。
       每个ArrayList实例都有一个容量,该容量是指用来存储列表元素的数组的大小。它总是至少等于列表的大小。随着向ArrayList中不断添加元 素,其容量也自动增长。自动增长会带来数据向新数组的重新拷贝,因此,如果可预知数据量的多少,可在构造ArrayList时指定其容量。在添加大量元素 前,应用程序也可以使用ensureCapacity操作来增加ArrayList实例的容量,这可以减少递增式再分配的数量。
       注意,此实现不是同步的。如果多个线程同时访问一个ArrayList实例,而其中至少一个线程从结构上修改了列表,那么它必须保持外部同步。

    2. ArrayList的实现:

       对于ArrayList而言,它实现List接口、底层使用数组保存所有元素。其操作基本上是对数组的操作。下面我们来分析ArrayList的源代码:

    • 底层使用数组实现
        private transient Object[] elementData;
    • 构造方法

      可以构造一个默认初始容量为0的空列表、构造一个指定初始容量的空列表以及构造一个包含指定collection的元素的列表,这些元素按照该collection的迭代器返回它们的顺序排列的。

     public ArrayList(int initialCapacity) {
            super();
            if (initialCapacity < 0)
                throw new IllegalArgumentException("Illegal Capacity: "+
                                                   initialCapacity);
            this.elementData = new Object[initialCapacity];
        }
    /**
         * Constructs an empty list with an initial capacity of ten.
         */
        public ArrayList() {
            super();
            this.elementData = EMPTY_ELEMENTDATA;
        }

        /**
         * Shared empty array instance used for empty instances.
         空数组
    */ private static final Object[] EMPTY_ELEMENTDATA = {};
     /**
         * Constructs an empty list with an initial capacity of ten.
         */
        public ArrayList() {
            super();
            this.elementData = EMPTY_ELEMENTDATA;
        }
    • 增加
    //将指定的元素添加到元素的尾部    
    public boolean add(E e) {
         //扩容 ensureCapacityInternal(size
    + 1); // Increments modCount!! elementData[size++] = e; return true; }
    //将指定的元素插入此列表中指定的位置
    public void add(int index, E element) {
        //如果(index >size || index < 0)抛出
    IndexOutOfBoundsException
    rangeCheckForAdd(index);
         //数组长度不足,进行扩容
            ensureCapacityInternal(size + 1);  // Increments modCount!!
        //将elementData数组中从index位置开始,长度为size - index 的元素拷贝到,从下面index+1开始新的elementData中
        //即将当前位于当前位置的元素及后面的元素向右移动一个位置 System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = element; size++; }

    // 按照指定collection的迭代器所返回的元素顺序,将该collection中的所有元素添加到此列表的尾部。  

    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;
        }
    // 从指定的位置开始,将指定collection中的所有元素插入到此列表中。
    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; }
    • 查看
    //返回列表中指定位置的元素
    public E get(int index) { rangeCheck(index); return elementData(index); }
    • 删除
    //移除指定位置上的元素,返回指定位置上原来的元素
    public E remove(int index) {
         //数组越界检测 rangeCheck(index); modCount
    ++; E oldValue = elementData(index);      //移除指定位置上的元素,底层数组copy,数组元素向左移动一个位置 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 return oldValue; }
    //移除第一次出现的指定的元素(ArrayList中允许存放重复元素)
    public
    boolean remove(Object o) {
         // 由于ArrayList中允许存放null,因此下面通过两种情况来分别处理。
    if (o == null) { for (int index = 0; index < size; index++) if (elementData[index] == null) {
                // 类似remove(int index),移除列表中指定位置上的元素。 fastRemove(index);
    return true; } } else { for (int index = 0; index < size; index++) if (o.equals(elementData[index])) { fastRemove(index); return true; } } return false; }
    • 修改:
    //用指定元素修改此列表中指定位置上得元素,并返回以前位于该位置上的元素
    1
    public E set(int index, E element) { 2 rangeCheck(index); 3 4 E oldValue = elementData(index); 5 elementData[index] = element; 6 return oldValue; 7 }
    • 数组的扩容

    从上面介绍的向ArrayList中存储元素的代码中,我们看到,每当向数组中添加元素时,都要去检查添加后元素的个数是否会超出当前数组的长度,如果超 出,数组将会进行扩容,以满足添加数据的需求。数组扩容通过一个公开的方法ensureCapacity(int minCapacity)来实现。在实际添加大量元素前,我也可以使用ensureCapacity来手动增加ArrayList实例的容量,以减少递增式再分配的数量。

     private void ensureCapacityInternal(int minCapacity) {
            if (elementData == EMPTY_ELEMENTDATA) {
                minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
            }
    
            ensureExplicitCapacity(minCapacity);
        }
    
        private void ensureExplicitCapacity(int minCapacity) {
            modCount++;
    
            // overflow-conscious code
            if (minCapacity - elementData.length > 0)
           //扩容方法 grow(minCapacity); }
    /** * The maximum size of array to allocate. * Some VMs reserve some header words in an array. * Attempts to allocate larger arrays may result in * OutOfMemoryError: Requested array size exceeds VM limit */ private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; /** * Increases the capacity to ensure that it can hold at least the * number of elements specified by the minimum capacity argument. * * @param minCapacity the desired minimum capacity
        扩容的主要方法
    */ private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length;
         //新的数组容量约等于原有的数组容量的1.5倍,oldCapacity >> 1右移一位,newCapacity = oldCapacity + oldCapacity/2
    int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); }
    //提供的公有的扩展容量的方法,可以手动增加ArrayList容量,可以减少递增式的再分配的数量
    public
    void ensureCapacity(int minCapacity) { int minExpand = (elementData != EMPTY_ELEMENTDATA) // any size if real element table ? 0 // larger than default for empty table. It's already supposed to be // at default size. : DEFAULT_CAPACITY; if (minCapacity > minExpand) { ensureExplicitCapacity(minCapacity); } }

     从上述代码中可以看出,数组进行扩容时,会将老数组中的元素重新拷贝一份到新的数组中,每次数组容量的增长大约是其原容量的1.5倍。这种操作的代价是很 高的,因此在实际使用时,我们应该尽量避免数组容量的扩张。当我们可预知要保存的元素的多少时,要在构造ArrayList实例时,就指定其容量,以避免 数组扩容的发生。或者根据实际需求,通过调用ensureCapacity方法来手动增加ArrayList实例的容量。

     
  • 相关阅读:
    SQLServer2012数据库降级至SQLServer2008R2的方法
    男程序猿和女程序猿的网恋—相见(二)
    收藏关于AI的相关的文章
    JAVA提高九:集合体系
    JAVA提高八:动态代理技术
    JAVA提高七:类加载器
    JAVA提高六:泛型
    JAVA提高五:注解Annotation
    JAVA提高四:反射基本应用
    JAVA提高三:反射总结
  • 原文地址:https://www.cnblogs.com/200911/p/4303416.html
Copyright © 2011-2022 走看看