ArrayList的源码分析

在项目中经常会用到list集合来存储数据,而其中ArrayList是用的最多的的一个集合,这篇博文主要简单介绍ArrayList的源码分析,基于JDK1.7:

这里主要介绍 集合 的属性,构造器,和方法:方法主要基于 add(E e),get(int index),remove(int index),set(int index,E e);

其中,E指的是泛型;

1:属性:

  /**
     * Default initial capacity.
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * Shared empty array instance used for empty instances.
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == EMPTY_ELEMENTDATA will be expanded to
     * DEFAULT_CAPACITY when the first element is added.
     */
    private transient Object[] elementData;

    /**
     * The size of the ArrayList (the number of elements it contains).
     *
     * @serial
     */
    private int size;

首先对几个修饰符进行说明一下:

final:修饰常量,表示不可变常量;

transient :修饰的字段,不会被序列化;

从  private transient Object[] elementData; 这个字段中可以看出ArrayList底层维护的是一个数组,该数组可以存放任何的Object类型;

DEFAULT_CAPACITY :说明集合的默认长度是10;但是在后续的源码跟踪中没有发现 集合的默认长都为10 的这一体现,希望有大神能够告知;

EMPTY_ELEMENTDATA = {} 不可变空数组,用于方法中的共享;

size:集合的包含元素的长度;

2:构造器:

 /**
     * Constructs an empty list with the specified initial capacity.
     *
     * @param  initialCapacity  the initial capacity of the list
     * @throws IllegalArgumentException if the specified initial capacity
     *         is negative
     */
    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;
    }

    /**
     * Constructs a list containing the elements of the specified
     * collection, in the order they are returned by the collection's
     * iterator.
     *
     * @param c the collection whose elements are to be placed into this list
     * @throws NullPointerException if the specified collection is null
     */
    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        size = elementData.length;
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    }

比较常用的有 

public ArrayList(int initialCapacity)  和 public ArrayList();所以这里只都这两个构造器进行分析;

public ArrayList(int initialCapacity);构造时指定集合的初始化大小;将构建好的集合赋给属性:

this.elementData=new Object[initialCapacity];以便在其他的方法中可以用这个集合;如果initialCapacity<0则会抛出参数非法的异常;

public ArrayList();其中:this.elementData = EMPTY_ELEMENTDATA;构建一个空集合,当然注释不是特别明白: Constructs an empty list with an initial capacity of ten:初始化容量为10的空集合

3:方法:

add(E e);以空集合和集合长度为10为例:

1):空集合:int size=0; 

调用的方法: ensureCapacityInternal(size + 1);参数为 1;

方法的源码:

private void ensureCapacityInternal(int minCapacity) {  //  minCapacity=1
        if (elementData == EMPTY_ELEMENTDATA) {  true
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);  //minCapacity=10
        }

        ensureExplicitCapacity(minCapacity);   //minCapacity=10
    }
接下来调用这个方法:ensureExplicitCapacity(minCapacity)  // minCapacity=10
private void ensureExplicitCapacity(int minCapacity) {  //minCapacity=10
        modCount++;            //记录构造器调用次数    

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)   //elementData.length=0
            grow(minCapacity); 
    }

接下来调用  grow(minCapacity);  //minCapacity=10

源码:

private void grow(int minCapacity) {             //minCapacity=10
        // overflow-conscious code
        int oldCapacity = elementData.length;      //oldCapacity=0
        int newCapacity = oldCapacity + (oldCapacity >> 1); //newCapacity=0
        if (newCapacity - minCapacity < 0)             // newCapacity - minCapacity=-10
            newCapacity = minCapacity;                 // newCapacity=10
        if (newCapacity - MAX_ARRAY_SIZE > 0)          //MAX_ARRAY_SIZE=2147483639
            newCapacity = hugeCapacity(minCapacity);   
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity); // elementData[]={},newCapacity=10
    }
 public static <T> T[] copyOf(T[] original, int newLength) {
        return (T[]) copyOf(original, newLength, original.getClass());
    }

 接下来分析 copyOf 的源码;

public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {  // original={},newLength=10,
        T[] copy = ((Object)newType == (Object)Object[].class)                             //如果 newType 为 Object[]类型返回Object[10] 如果不是则构造Object[10]返回
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }
System.arraycopy(original, 0, copy, 0, 10);  // original={}   copy=Object[10]
源码到这里就不能再继续跟踪下去了  arraycopy() 方法是被native修饰的,是以c写成的语言;
根据注释中可以知道方法的作用:

original:源数组 0:原数组的其实位置 copy:目标数组 0:目标数组的起始位置 10:需要拷贝的数组个数
将源数组拷贝到目标数组中,此时:集合中的数组为 Object[10];
接下来:
elementData[size++] = e; //size++=1
elementData[1]=e;
2):集合的原长度是10,此时需要增加一个元素:
public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!  size=10 size+1=11
        elementData[size++] = e;
        return true;
    }
private void ensureCapacityInternal(int minCapacity) {   //minCapacity=11
        if (elementData == EMPTY_ELEMENTDATA) {     //false
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }

 

 private void ensureExplicitCapacity(int minCapacity) {   // minCapacity=11
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)    //true   当数组中的元素个数等于数组自定长度时则返回true 因为此时 minCapacity=elementData.length+1 否则返回 false
            grow(minCapacity);
    }

 

private void grow(int minCapacity) {       //minCapacity=11
        // overflow-conscious code
        int oldCapacity = elementData.length;  //oldCapacity=10
        int newCapacity = oldCapacity + (oldCapacity >> 1);  //newCapacity=15
        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);
    }
Arrays.copyOf(elementData, newCapacity);内部的实现方法:新建一个长度为15的数组,将旧数组的内容拷贝到新的数组中;拷贝到新数组中从0开始作为起始位;
到此 add(E e)方法的源码就分析完了;

接下来分析get(int index)的源码:
public E get(int index) {
        rangeCheck(index);   //检查是否数组越界了

        return elementData(index);  //返回指定的索引的元素
    }

  

private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

 

接下来分析remove(int index) 的方法:

 public E remove(int index) {
        rangeCheck(index);   //检查数组是否越界

        modCount++;
        E oldValue = elementData(index);  //获取指定索引的元素

        int numMoved = size - index - 1;   //这个索引之后的所有元素
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);    //将源数组从index+1的元素移到目标数组从index为开始的位置,移动的元素个数为 numMoved
        elementData[--size] = null; // clear to let GC do its work  

        return oldValue;
    }

接下来是set(int index,E e)

 public E set(int index, E element) {
        rangeCheck(index);    //检查是否数组越界

        E oldValue = elementData(index);  //获得原先的元素
        elementData[index] = element;     //将element元素赋值给索引是index的元素
        return oldValue;
    }

ok了;

mark:调用集合的add(E e)方法时,当需要添加的元素个数超出数组的空余长度时则需要扩充数组的长度,每次扩充的长度是原来数组的1.5倍(jdk1.6之前的版本则是原来数组的1.5倍+1),扩充后的数组是构创建一个新数组,原先的数组则会被回收;让后将原先就数组的元素拷贝到新数组中,这样递归地扩充拷贝很影响集合的运行效率,所以当知道集合元素个数时,在出事后集合大小的时候指定集合的长度,这样开销会小很多;否则则可以考虑用linkedList来存储数据;

查询是基于索引查询的,用ArrayList效率会很快  

 




  

 

 

  









  

 

posted @ 2017-03-16 17:34  beppezhang  阅读(421)  评论(0编辑  收藏  举报