zoukankan      html  css  js  c++  java
  • 【java集合框架源码剖析系列】java源码剖析之ArrayList

    注:博主java集合框架源码剖析系列的源码全部基于JDK1.8.0版本。

    本博客将从源码角度带领大家学习关于ArrayList的知识。

    一ArrayList类的定义:

    public class ArrayList<E> extends AbstractList<E>
            implements List<E>, RandomAccess, Cloneable, java.io.Serializable
    从上述代码可以看到:ArrayList继承自AbstractList同时实现了List,RandomAccess,Cloneable与Serializable接口

    二ArrayList类一些重要属性:

     private static final int DEFAULT_CAPACITY = 10;
    
     private static final Object[] EMPTY_ELEMENTDATA = {};
    
     private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    
     transient Object[] elementData; 
    
     private int size;
    从这里可以看到ArrayList全部操作是基于Object[]数据结构的,即ArrayList底层是基于数组实现的,因此具备较好的随机访问的能力

    三ArrayList内部实现原理:我们先来看一下ArrayList的构造器

    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);
            }
        }
    
    public ArrayList() {
            this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
        }
    
    
     public ArrayList(Collection<? extends E> c) {
            elementData = c.toArray();
            if ((size = elementData.length) != 0) {
                // c.toArray might (incorrectly) not return Object[] (see 6260652)
                if (elementData.getClass() != Object[].class)
                    elementData = Arrays.copyOf(elementData, size, Object[].class);
            } else {
                // replace with empty array.
                this.elementData = EMPTY_ELEMENTDATA;
            }
        }
    可以看到当用一个集合作为参数去初始化一个ArrayList的时候,其内部处理过程与LinkedList非常类似,即先将集合转化为数组,然后将元素复制到自己内部定义的数组elementData数组中。返回的若不是Object[]将调用Arrays.copyOf方法将其转为Object[]。Arrays.copyOf代码如下:

     public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
            @SuppressWarnings("unchecked")
            T[] copy = ((Object)newType == (Object)Object[].class)
                ? (T[]) new Object[newLength]
                : (T[]) Array.newInstance(newType.getComponentType(), newLength);
            System.arraycopy(original, 0, copy, 0,
                             Math.min(original.length, newLength));
            return copy;
        }

    四ArrayList一些重要的函数:

    1add(E e)与add(int index,E element)

    public boolean add(E e) {
            ensureCapacityInternal(size + 1);  // Increments modCount!!
            elementData[size++] = e;
            return true;
        }
    
     public void add(int index, E element) {
            rangeCheckForAdd(index);
    
            ensureCapacityInternal(size + 1);  // Increments modCount!!
            System.arraycopy(elementData, index, elementData, index + 1,
                             size - index);
            elementData[index] = element;
            size++;
        }

    其中add(E e)表示在ArrayList的末尾添加一个元素,而add(int index,E element)表示在index处添加一个元素,其中为了防止当数组中的容量不够的情况,会调用ensureCapacityInternal(size + 1); 函数用来确保向数组中添加元素的时候数组大小始终足够。代码如下;

     private void ensureCapacityInternal(int minCapacity) {
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
                minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
            }
    
            ensureExplicitCapacity(minCapacity);
        }
    而在该函数中调用了ensureExplicitCapacity(int minCapacity)函数,我们来看一下其源码:

     private void ensureExplicitCapacity(int minCapacity) {
            modCount++;
    
            // overflow-conscious code
            if (minCapacity - elementData.length > 0)// 超出了数组可容纳的长度,则进行动态扩展,即调用grow函数
                grow(minCapacity);
        }
     private void grow(int minCapacity) {
            // overflow-conscious code
            int oldCapacity = elementData.length;
            int newCapacity = oldCapacity + (oldCapacity >> 1);//将oldCapacity除以2在加上自己,即新的容量为原来的1.5倍
            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);
        }
    综上,当调用add()添加元素时,add方法先调用ensureCapacityInternal方法增加自身容量(注意此时仅仅是minCapicity的值加1,而数组内存还未扩容),本质上是通过grow()函数来完成的,在调用grow()函数时,先求出原数组的长度记为oldCapacity,然后将该值更新为oldCapacity+oldCapacity>>1即新的容量为原来容量的1.5倍.,将其记为newCapacity,如果扩容后的容量超出了数组可容纳的最大长度MAX_ARRAY_SIZE,则调用hugeCapacity()将其返回值 Integer.MAX_VALUE作为新的容量,然后调用系统的arraycopy函数将原数组的内容复制到新数组中返回。

    即add()方法中存在扩容机制,当通过add()方法添加一个元素时,会先调用ensureCapacityInternal方法来确保数组内存始终足够,本质上是通过grow函数来完成的,在grow()函数中会将原数组的长度更改为其原来的1.5倍,如果更改后的数组容量比传入的minCapacity小,则将更改后的容量更新为minCapacity(该值是期望的数组的最小容量,如当已存在1个元素然后添加一个元素则minCapacity的值为2即能容纳添加进去的元素的最小容量,扩容后的值newCapacity在绝大多数情况下比它大,但可能比它小,因为上述扩容机制采用的是扩大为1.5倍,而采用的是整除,如1+1/2=1,而minCapacity=size+1=2,此时newCapacity<minCapacity)

    2addAll(Collection<? extends E> c) 与addAll(int index, Collection<? extends E> c) 

     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;
        }
    
     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;
        }
    
    可以看到,addAll()与add()方法几乎完全相同,只不过存在一个将集合转化为数组的过程,这一点与LinkedList非常类似,然后接下来的操作与add操作完全相同,即

    调用ensureCapacityInternal扩容,如果插入位置不是Arraylist的末尾,则调用系统的arraycopy函数将索引处及其后的元素后移一位在index处插入该元素。处理完毕后将临时数组a中的元素arraycopy到elementData中。

    3Object[] toArray() 

     public Object[] toArray() {
            return Arrays.copyOf(elementData, size);
        }
    
     public <T> T[] toArray(T[] a) {
            if (a.length < size)
                // Make a new array of a's runtime type, but my contents:
                return (T[]) Arrays.copyOf(elementData, size, a.getClass());
            System.arraycopy(elementData, 0, a, 0, size);
            if (a.length > size)
                a[size] = null;
            return a;
        }
    该函数的作用就是返回一个包含list中所有元素的数组,具体实现过程如下:

    如果传入数组的长度length小于size,返回一个新的数组,大小为size,类型与传入数组相同;
    否则将elementData的元素全部复制到传入的数组a中,并返回a;
    若length大于size,除了复制elementData外,还将把返回数组的第size个元素置为空。
    4void sort(Comparator<? super E> c)

    public void sort(Comparator<? super E> c) {
            final int expectedModCount = modCount;
            Arrays.sort((E[]) elementData, 0, size, c);
            if (modCount != expectedModCount) {
                throw new ConcurrentModificationException();
            }
            modCount++;
        }

    可以看到该sort函数是基于Comparator<? super E> c接口的,即待排序的元素必须实现Comparator接口,其实不仅仅是ArrayList,java集合框架中的全部集合的排序都是基于

    Comparator接口的。

    五总结:

    1ArrayList的内部是基于Object[]类型的数组实现的,支持下标访问,因此具备较高的随机访问速度。

    2当用一个集合作为参数来构造一个ArrayList时,其内部是是先将该集合转化为数组,这一点与LinkedList非常类似,与HashMap一样,ArrayList也存在扩容机制,即调用ensureCapacityInternal扩容

    3可以看到ArrayList中的方法都未使用synchronized关键字修饰,即ArrayList是非同步的。

    4ArrayList允许重复元素存在,因为在add元素的过程中不存在HashMap中put时判重替换的过程,只是进行简单的插入操作。






  • 相关阅读:
    GUI学习笔记之一“Hello world”程序
    GDI和GUI的区别
    Convert.Int32、(int)和int.Parse三者的区别
    华为机试题汇总
    算法导论 第7章 课后习题
    算法导论 第8章 线性时间排序 课后习题
    算法导论 第21章 不相交集合的数据结构
    [转载]NIM(1) 一排石头的游戏
    算法导论 第22章 图论之拓扑排序
    编程珠玑第八章 算法设计艺术
  • 原文地址:https://www.cnblogs.com/hainange/p/6334038.html
Copyright © 2011-2022 走看看