源碼分析|ArrayList

1.什麼是ArrayLsit

ArrayList是JDK中的一個基於數組實現的線性的可變長度的集合類,而且實現了List接口。數組

2.ArrayList構造方法分析

不指定容量時

//建立ArrayList
ArrayList list = new ArrayList();
複製代碼

建立完成後,點擊進入ArrayList源碼,能夠發現是bash

public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
複製代碼

這樣的一個無參構造方法對ArrayList進行了初始化操做,其中elementData是一個Object類型的數組,DEFAULTCAPACITY_EMPTY_ELEMENTDATA也是一個Object類型的數組且長度爲0。函數

指定容量時

//建立ArrayList
ArrayList list = new ArrayList(20);
複製代碼

再次點擊就會發現會調用一個制定了初始化容量的構造方法ui

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);
    }
}
複製代碼

根據代碼可知,咱們傳入參數後會建立一個容量爲20的數組。若是傳入的不是20而是0的話,就會建立一個容量爲空的數組,也就是this.elementData = {}this

3.add()方法

//建立ArrayList
ArrayList list = new ArrayList();
list.add("aaa");
複製代碼

點擊進入以後能夠發現以下代碼spa

public boolean add(E e) {
    //用於檢測容量是否夠用
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    //將元素添加進集合中,而且維護size的值。  size的初始值爲0
    elementData[size++] = e;
    return true;
}
複製代碼

點擊ensureCapacityInternal(size + 1);會發現以下代碼code

private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
複製代碼

從代碼中能夠看出傳入的是添加元素須要的最小空間,而後再點擊calculateCapacity方法對象

private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        //DEFAULT_CAPACITY = 10
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}
複製代碼

發現傳入的是一個Object類型的數組和須要的最小容量。由於elementData是空的,因此會進入第一個if判斷,這樣會從10和0之間選取最大值,結果顯而易見,因此10被返回到ensureExplicitCapacity的參數列表中。索引

接着再點擊ensureExplicitCapacity接口

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        //擴容函數
        grow(minCapacity);
}
複製代碼

從這裏能夠看出會將剛纔傳入的10傳到擴容的函數中,再點擊grow

private void grow(int minCapacity) {    //minCapacity = 10
    //elementData是一個空數組,因此oldCapacity爲0
    int oldCapacity = elementData.length;
    // newCapacity = 0 + 0      因此newCapacity = 0
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    // 0 - 10 < 0  true
    if (newCapacity - minCapacity < 0)
        // newCapacity = 10
        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);
}
複製代碼

從中得出newCapacity = 10,而後調用copyOf方法進行復制,複製完成就會將elementData的長度設置爲10。

總結:在第一次調用add()方法時,會將容量初始化爲10,可是若是老是執行添加操做,就會再次到達ArrayList的擴容標準

向list中添加元素到11次

//建立ArrayList
    ArrayList list = new ArrayList();

    list.add("aaa");
    list.add("aaa");
    list.add("aaa");
    list.add("aaa");
    list.add("aaa");
    list.add("aaa");
    list.add("aaa");
    list.add("aaa");
    list.add("aaa");
    list.add("aaa");
    list.add("aaa");    //第11次調用add()方法
複製代碼

此時咱們再執行上面的操做,直到這裏

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    // 11 - 10 > 0    true
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}
複製代碼

再次執行擴容操做

private void grow(int minCapacity) {    //minCapacity = 11
    // oldCapacity = 10
    int oldCapacity = elementData.length;   
    //newCapacity = 10 + 10 / 2    15
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    // 15 - 11 < 0   false
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // 進行copy
    elementData = Arrays.copyOf(elementData, newCapacity);
}
複製代碼

當拷貝完成擴容操做執行完畢,elememtData的容量此時爲15

總結:當容量進行擴容時會將容量擴容爲原來的1.5

4.get()方法

System.out.println(list.get(2));
複製代碼

get方法須要傳入數組下標,並經過下標返回數組中的值。點擊get方法會顯示以下內容

public E get(int index) {
    //檢查下標合法性,不合法則拋出異常
    rangeCheck(index);
    
    //直接返回數組中下標爲index的元素
    return elementData(index);
}
複製代碼

5.set()方法

list.set(2, "bbb");
複製代碼

這裏表示想將數組中下標爲2的元素設置爲bbb。點擊進入

public E set(int index, E element) {
    //檢查下標合法性
    rangeCheck(index);

    //保存索引爲index位置的值
    E oldValue = elementData(index);
    //將傳入的元素設置在index位置
    elementData[index] = element;
    //返回保存的元素
    return oldValue;
}
複製代碼

經過傳入index和E類型的元素,更新完成後將原來的元素值返回。

6.remove()方法

System.out.println(list.remove(2));
複製代碼

刪除下標爲2的元素,並將刪除前的元素返回,點擊remove()方法

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);
    elementData[--size] = null; // clear to let GC do its work

    return oldValue;
}
複製代碼

7.iterator()方法

//建立ArrayList
    ArrayList list = new ArrayList();

    list.add("aaa");
    list.add("bbb");
    list.add("ccc");

    //建立迭代器
    Iterator it = list.iterator();
    //判斷迭代器中是否還有下一個元素
    while (it.hasNext()){
        //將對象取出
        Object o = it.next();
        System.out.println(o);
    }
複製代碼

迭代器能夠遍歷ArrayList中的元素,點擊iterator()

public Iterator<E> iterator() {
    return new Itr();
}
複製代碼

在迭代器內部建立了一個Itr對象,再點擊

//指向元素的遊標,當前cursor = 0
int cursor;       // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;

Itr() {}

//迭代器中是否包含下一個元素
public boolean hasNext() {
    return cursor != size;
}

//將遊標向後移動
public E next() {
    checkForComodification();
    int i = cursor;
    if (i >= size)
        throw new NoSuchElementException();
    Object[] elementData = ArrayList.this.elementData;
    if (i >= elementData.length)
        throw new ConcurrentModificationException();
    //將cursor向後移動
    cursor = i + 1;
    return (E) elementData[lastRet = i];
}
複製代碼

進入以後關注以上方法及參數 由於上面添加了3個元素,因此size爲3,cursorsize不相等,返回true,因此會進入while循環並執行next方法,首先會將cursor賦值給i,此時i爲0,而後cursor向後移動最後將下標爲0的元素返回。一直循環下去直至hasNext返回false

相關文章
相關標籤/搜索