ArrayList分析

超殺女.jpg

對於集合的源碼分析,通常我會採用這幾種方式git

  1. 怎麼添加元素?
  2. 怎麼獲取元素?
  3. 怎麼刪除元素?
  4. 內部數據結構實現?

話很少說,直接走起。github

一.怎麼添加元素?

通常咱們經過ArrayList添加元素。通常會調用其構造方法,而後調用其對象的add方法數組

查看空參構造函數

//Constructs an empty list with an initial capacity of ten.
 public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
複製代碼

經過構造函數能夠發現。ArrayList在調用無參的構造函數時,會構造一個長度爲10的緩存數組緩存

查看add方法

public boolean add(E e) {
        ensureCapacityInternal(size + 1); 
        elementData[size++] = e;
        return true;
    }
複製代碼

經過該方法發現 ArralyList內部的數據結構實際上是一個數組(elementData[size++] = e;)而且在添加時會先判斷當前容器在添加了一個對象以後該對象的容納能力(主要爲了,在下一次添加元素的時候,緩存數組可以有足夠的空間添加元素)。以後將元素添加到數組末尾。安全

繼續查看ensureCapacityInternal()方法

private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }
複製代碼

這裏咱們發現,若是當前elementData爲空的話,minCapacity=DEFAULT_CAPACITY,同時DEFAULT_CAPACITY的默認值是10,從這咱們能夠看出,在第一次初始化的時候,ArrayList內部會默認建立一個內部長度爲10的數組。bash

繼續點擊ensureExplicitCapacity()方法

private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        //判斷添加元素後,緩存數組時候須要擴展
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
複製代碼

該方法會記錄當前數組的更改次數,而且判斷當前數組添加後,是否須要進行增加,數據結構

繼續走grow方法(重點的來了!!!)

private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        //擴展數組的長度,
        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);
    }
複製代碼

該方法會擴展緩存爲當前數組的長度爲 原數組長度+原數組長度的二分之一,也就是按照原數組的50%進行增加,同時該數組最大的擴展長度是Integer.MAX_VALUE - 8。也就是ArrayList最多能存儲的數據長度,經過擴展數組長度之後,在下一次添加數據的時候,ArrayList就有足夠的空間去添加新的元素了。函數

二.怎麼獲取元素

其實ArrayList獲取其中的元素很簡單,根據角標獲取對應數組中的元素,具體代碼以下:源碼分析

public E get(int index) {
        if (index >= size)//判斷當前角標長度是否超過數組長度,若是是拋出異常,反之返回數據
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
        return (E) elementData[index];
    }

複製代碼

三.怎麼刪除元素

在ArrayList中,有兩個關於刪除元素的方法,一個是remove(int),另外一個是remove(Object)ui

1.remove(int)方法

public E remove(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

        modCount++;
        E oldValue = (E) elementData[index];

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // 將數組最後一位置null

        return oldValue;
    }
複製代碼

這裏先判斷刪除角標是否超過數組長度,而後經過System.arrayCopty()方法將角標對應的元素刪除。

這裏對System.arrayCopty()方法解釋一下。該方法的第一個參數是源數組,第二個參數是複製的開始角標,第二個參數是目標數組。第三個參數是目標數組源數組的複製數據開始角標。最後一個參數是複製的長度。(注意:!!!複製的長度不能大於目標數組減去開始角標的長度源數組減去開始角標的長度)

eg:

int[] a = {0, 1, 2, 3, 4};
  int[] b = {5, 6, 7, 8, 9};
  System.arraycopy(a, 0, b, 1, 3);
 // 則進行操做後 b = {5,0,1,2,9} 
複製代碼

2.remove(Object)方法

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

remove(Object)根據object在數組的角標,執行fastRemove(index)方法。刪除方法與remoIndex(int)同樣。這裏就不在分析了。

總結

  • ArrayList內部實現是數組,且當數組長度不夠時,數組的會進行原數組長度的1.5倍擴容
  • ArrayList內部元素是能夠重複的。且有序的,由於是按照數組一個一個進行添加的。
  • ArrayList是線程不安全的,由於其內部添加、刪除、等操做,沒有進行同步操做。
  • ArrayList增刪元素速度較慢,由於內部實現是數組,每次操做都會對數組進行復制操做,複製操做是比較耗時的

最後,附上我寫的一個基於Kotlin 仿開眼的項目SimpleEyes(ps: 其實在我以前,已經有不少小朋友開始仿這款應用了,可是我以爲要作就作好。因此個人項目和其餘的人應該不一樣,不單單是簡單的一個應用。可是,可是。可是。重要的話說三遍。還在開發階段,不要打我),歡迎你們follow和start

相關文章
相關標籤/搜索