Java集合框架解析(2) - 深入ArrayList源碼

ArrayList 是我們常用的集合之一。從名稱可以看出,ArrayList 必然和Array有著不可或缺的聯系。

我們看看ArrayList 比較核心的操作

  1. 以什么樣的數據結構來存儲數據的?
  2. 怎么初始化的?做了哪些事情?
  3. add中做了哪些操作呢?
  4. add中的擴容怎么擴容的?
  5. 怎么get數據呢?

我們先看看在ArrayList中的一些操作性的東西

默認參數

private static final int DEFAULT_CAPACITY = 10; // 默認數組容量
private static final Object[] EMPTY_ELEMENTDATA = {}; //空Object數組
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; //空Object數組 初始化使用
transient Object[] elementData;  //存儲數據的數組
private int size; // 實際有值的容量
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; // 建議的數組容量值

初始化

初始化分為三種情況

  1. 無參數初始化;
    將存儲對象的數組設置為空的Object數組
  2. 根據容量初始化;
    根據傳遞的容量大小初始化容量,并初始化為Object數組
  3. 根據集合數據初始化;
    將集合轉為數組后,設置給存儲數組,然后將數組轉換為Object數組

無參數就好比我們做老式大巴車一樣,生意不好的時候只有一個空車,連座位都沒有這種,等有人上車再給一個個安排座位;司機如果懶就先放了些空位,等后面有人上車不用安排座位了,直接坐就好了,等空位滿了再一個個安排,這就是容量初始化;過年過節人多了,就直接所有人上車,然后安排各自座位;

// 默認無參初始化
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;//設置為空數組
}

// 初始化容量的實現
public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
          this.elementData = new Object[initialCapacity]; // 初始化 Object數組
    } else if (initialCapacity == 0) {
         this.elementData = EMPTY_ELEMENTDATA; //設置為空Object數組
    } else {
         throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
    }
}

// 參數為集合的初始化
public ArrayList(Collection<? extends E> c) {
     elementData = c.toArray(); //將傳遞的集合轉數組
     if ((size = elementData.length) != 0) {
        if (elementData.getClass() != Object[].class) // 非Object 數組
              elementData = Arrays.copyOf(elementData, size, Object[].class);//將數組轉為Object數組
    } else {
       this.elementData = EMPTY_ELEMENTDATA; //設置為空Object數組
   }
}

add ?

當有人上車,會出現兩種情況;
(1) 有人上車呢,隨便做,司機就給安排到最后做去了;
(2) 有人非要根據座位號去坐車,怎么辦呢?司機直接把座位后面的人一個個往后排了,然后給他讓出空位置給他坐;
無論這上面那種情況,都會涉及到一個問題,預先安排的空座位不夠了,需要加座位了,司機只有來一個人加一個,來一雙加兩個了。

看看代碼中是怎么實現的呢

/**
 *  根據下標添加數據
 * @param  e 添加的對象
 */
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++;
}

/**
 *  直接添加數據
 * @param  e 添加的對象
 */
public boolean add(E e) {
      // 驗證車(數組)時候還有位置,沒了則給車加位置(擴容),保證容量足夠,所有人都能坐著
     ensureCapacityInternal(size + 1); 
     elementData[size++] = e; // 將入參對象追加到末尾,并對size + 1
     return true;
}

/**
 * 處理容量
 * @param minCapacity 最小容量
 */
private void ensureCapacityInternal(int minCapacity) {
     // 先調用計算容量函數
     ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

/**
 *  計算需要的座位數(容量)
 * @param minCapacity 需要的最小容量
 */
private static int calculateCapacity(Object[] elementData, int minCapacity) {
      // 判斷數組是否為初始化的Object 數組 ,是則根據 需要的最小容量 和默認容量取大值,不是則返回最小容量
      if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
         return Math.max(DEFAULT_CAPACITY, minCapacity);
      }
      return minCapacity;
}

/**
 *  判斷有沒有位置,沒位置則加位置(擴容)
 * @param minCapacity 最小容量
 */
private void ensureExplicitCapacity(int minCapacity) {
      modCount++;  // modCount 計數
    
     // 當最小容量大于數組的容量時,進行擴容操作
     if (minCapacity - elementData.length > 0)
          grow(minCapacity); //擴容函數
}

擴容?

司機在安排新座位的時候,都會重新給在車上的人再安排一下,來達到加座位的目的;智障啊!沒辦法,程序就是這么智障

/**
 *  擴容
 * @param minCapacity 需要的最小容量
 */
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);// 對已經存在的數組進行擴容copy
}

/**
 *  獲取車最大的載客數
 * @param minCapacity 所有的人(需要的容量)
 */
private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    // 當需要的容量大于數組的容量時,返回最大的容量為 Integer.MAX_VALUE 否則 MAX_ARRAY_SIZE
    return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; 
}

get 呢 ?

車到達目的地后,看看有哪些人該下車了,直接根據座位號就可以找到人,然后通知下車咯!

public E get(int index) {
    rangeCheck(index); //驗證長度
    return elementData(index);//返回 index 的值
}

/**
 *  驗證index的值是否大于當前數據的總容量值
 * @param index 下標
 */
private void rangeCheck(int index) {
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

/**
 *  根據下標獲取數據
 * @param index 下標
 */
E elementData(int index) {
    return (E) elementData[index]; //獲取index 的值
}

我們來分析下結論

  1. ArrayList 的底層數據結構為Array;
  2. ArrayList 和Array 一樣,下標都是從0開始;
  3. 數組默認的初始化容量大小為 10;
  4. 當Array的沒有空位置的時候,每次新增,都會進行擴容操作,效率是及其低下的;
  5. 如果在數據已知的情況下可以初始化數組的大小,可以節約性能,不過也不可以盲目的設置初始化大小值,以免占用空間;

可以關注我的公眾號哦!
進階全棧.jpg
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,321評論 6 543
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,559評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,442評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,835評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,581評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,922評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,931評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,096評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,639評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,374評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,591評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,104評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,789評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,196評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,524評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,322評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,554評論 2 379