java(6) ArrayList源码

系统环境: JDK 1.7

成员变量

//默认的初始化数组大小
private static final int DEFAULT_CAPACITY = 10;

//空的对象数组
private static final Object[] EMPTY_ELEMENTDATA = {};

//声明对象数组,不可序列化 transient
private transient Object[] elementData;

//数组的大小
private int size;

构造方法

1.带有容量initialCapacity的构造方法

public ArrayList(int initialCapacity) {
    super();
    //参数小于0 则抛出异常
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
    //new一个该大小的object数组赋给elementData  
    this.elementData = new Object[initialCapacity];
}

2.不带参数的构造方法

public ArrayList() {
    super();
    this.elementData = EMPTY_ELEMENTDATA;
}    

3.带参数Collection的构造方法

public ArrayList(Collection<? extends E> c) {
    //将 c 转换为对象数据并赋值给elementData
    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);
}    

注:c.toArray()返回的类型可能是非Object[]类型,如:Arrays.asList().toArray(),详情参考《关于 ArrayList.toArray()和Arrays.asList().toArray()方法

总结:通过三个构造方法 我们发现ArrayList的实质就是封装了对数组的一些操作,通过这些操作,从而达到我们需要的目的

Add方法分析

public boolean add(E e) {
    // 确保当前的数组大小可以装的下传入的对象e
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    // 在内部数组中存放对象,并将索引值+1
    elementData[size++] = e;
    return true;
}

public void add(int index, E element) {
    // 确保传入的数值没有越界
    rangeCheckForAdd(index);
    // 确保当前的数组大小可以装的下传入的对象e
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    elementData[index] = element;
    size++;
}    

private void ensureCapacityInternal(int minCapacity) {
    //针对new ArrayList()这种初始化方法有效,只有无参构造方法让内部数组等于empty。
    if (elementData == EMPTY_ELEMENTDATA) {
        // 确保 minCapacity 最小值是 10
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    //
    ensureExplicitCapacity(minCapacity);
}    


//判断是不是真的需要扩容。
//如果我们用new ArrayList()初始化,根据上边的代码 minCapacity=10,而现在内部类真正的大小elementData.length是 0,所以需要扩容。
//如果我们用new ArrayList(20)初始化,内部数组在构造方法内已经初始化了,内部数组长度为20,而在add方法的时候,第一个操作传入的是size+1,即0+1。
//所以到达这个判断的时候,最小需要的容量为1,而长度为20 ,必然不需要扩容    
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

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

//数组真正的扩容操作
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    
    // 新长度为原数组长度的1.5倍  整数右移1为 相当于除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);
}    

Remove方法分析

  remove方法有2个,传入的参数分别是int和Object

public E remove(int index) {
    //判断传入的数字是否在合理范围内,即是否小于数组内真实的数据个数
    rangeCheck(index);

    modCount++;
    E oldValue = elementData(index);//将要remove的索引位置的元素取出
    //将内部数组中空出来的那个位置之后的元素移动到前边去
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    // 将最后一位置空,size自减  
    elementData[--size] = null; // clear to let GC do its work

    return oldValue; // 返回移除的那个数据
}    

//另一个重载方法,实质上是一样的 
public boolean remove(Object o) {
    if (o == null) {
        for (int index = 0; index < size; index++)
            if (elementData[index] == null) {
                fastRemove(index);
                return true;
            }
    } else {
        for (int index = 0; index < size; index++)
            if (o.equals(elementData[index])) {
                fastRemove(index);
                return true;
            }
    }
    return false;
}    

//移除数据操作
private void fastRemove(int index) {
    modCount++;
    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
}

//判断传入的数字是否在合理范围内
private void rangeCheck(int index) {
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}    


@SuppressWarnings("unchecked")
E elementData(int index) {
    return (E) elementData[index];
}    

System.arraycopy()方法

*5个参数的含义分别是:
--src  源数组.
--srcPos  源数组中的起始位置。
--dest  目标数组。
--destPos  目标数据中的起始位置。
--length  要复制的数组元素的数目    
int[] arr1 = { 0, 1, 2, 3, 4, 5 };  
int[] arr2 = { 6, 7, 8, 9, 10, 11};  
// 将arr1的元素复制到arr2中,从arr1的索引位置为3开始,复制长度为1个,到arr2中,arr2从索引为0的位置开始接受复制  
System.arraycopy(arr1, 3, arr2, 0, 1);  
// 所以最后结果是--  arr1:{ 0, 1, 2, 3, 4, 5 }   arr2:{ 3, 7, 8, 9, 10, 11}  
System.out.println(Arrays.toString(arr1));  
System.out.println(Arrays.toString(arr2));

 

整理自《http://blog.csdn.net/zw0283/article/details/51122255》

posted @ 2017-07-12 14:17  PoleStar  阅读(215)  评论(0编辑  收藏  举报