1. 程式人生 > >集合擴容問題(ArrList為例,常用集合擴容機制) -- JAVA 基礎

集合擴容問題(ArrList為例,常用集合擴容機制) -- JAVA 基礎

今天剛好遇到一個關於集合擴容的問題,正好藉機整理一下:
當底層實現涉及到擴容時,容器或重新分配一段更大的連續記憶體(如果是離散分配則不需要重新分配,離散分配都是插入新元素時動態分配記憶體),要將容器原來的資料全部複製到新的記憶體上,如果是每增加一個元素就複製一次,這無疑使效率大大降低。這裡的擴容就是噹噹前容器的記憶體不足或者達到載入因子設定的係數時,需要對容器進行一次合適的擴建(載入因子的係數小於等於1,意指 即當 元素個數 超過 容量長度*載入因子的係數 時,進行擴容。有預設值)

List集合 (元素是有序的、可重複)

我們知道List集合的底層實現是陣列結構,而陣列的大小是不可改變的,因此當其容器記憶體不足時,需要進行擴容,擴容的方法就是重新分配一個新陣列,然後複製元素到新陣列中,再將元素新增到陣列末位
一、ArrayList


首先便是預設大小的問題
這裡我們可以通過ArrayList的原始碼來分析,首先可以看一下他的建構函式,發現有三個,分別是:無參、一個int型引數、一個集合泛型;
在分析建構函式之前 我們先來看幾個引數:
在
即ArrayList 在內部的表示即為一個Object型別的陣列,並非真正的泛型,這裡的變數就是陣列的真實儲存大小
這裡寫圖片描述
可以理解為設定預設陣列大小的常量

1、無參的建構函式
這裡寫圖片描述
在這裡我們可以看到,當不傳引數時,陣列的大小直接為預設陣列大小的常量,而這個大小就為10,至於怎麼得到的,這裡我們可以先賣個小關子,在後面一起介紹~~
2、有一個int的建構函式
這裡寫圖片描述
我們可以發現,當傳入的值大於0時,陣列的大小即為傳入的值大小,為0時,則為EMPTY_ELEMENTDATA,小於0則丟擲異常。這個EMPTY_ELEMENTDATA又是什麼意思呢??
其實就是0(即亦可理解為傳入的值);

3、有一個Collection泛型的建構函式
這裡寫圖片描述
而集合引數的其實與Int的建構函式差不多,只是在初始化容器的同時,還將容器中的資料也初始化了(完成copy)。

雖然說完了建構函式,但實際的擴容是發生在增加元素的時候(容器記憶體不足或者儲存達到載入因子),因此 來到了最為主要的一部分內容:
4、ArrayList 的元素新增
這裡寫圖片描述
通過上面的程式碼可以看到,每次新增元素的時候,都會執行一個方法,而這個方法,就是判斷是否需要擴容的方法;
這裡寫圖片描述
進入方法之後,我們看到了一個if語句,發現了麼,這個就是我們之前在無參建構函式時賣的關子,噹噹前的大小為預設陣列大小的常量時,minCapacity(即位陣列中儲存的資料大小即size+1)就為DEFAULT_CAPACITY, minCapacity之間大的那個,而minCapacity的預設值為0,所以從這裡就可以知道,無參建構函式產生的陣列大小為10~~
但是真正的判斷還不在這裡,而是下面這個方法:
這裡寫圖片描述


由此我們可以看出,其實就是判斷加一個元素後,陣列大小是夠超出當前陣列的大小,若超出,則執行下面的”擴容“程式碼~~
這裡寫圖片描述

從文中我們可看到,新陣列的大小其實就是( oldCapacity + (oldCapacity >> 1)),如果你懂得位運算的話,你就知道了,原來就是原陣列的長度加上原陣列的長度大小的一半(位運算,右移一位相當於整體/2),後面判斷陣列大小越界這裡就不多闡述了,感興趣的可以自己檢視一下原始碼。
上面的ArrayList是沒有涉及載入因子的,但是有些集合型別如Vector等使用到了載入因子,只要把是否需要擴容d的判斷條件改一下就可以了。
**

總結

**
ArrayList 預設初始容量為10
執行緒不安全,查詢速度快

    底層資料結構是陣列結構

    擴容增量:原容量的 0.5倍

    如 ArrayList的容量為10,一次擴容後是容量為15

同樣可以通過分析原始碼知道:
Vector:執行緒安全,但速度慢

    底層資料結構是陣列結構

    載入因子為1:即當 元素個數 超過 容量長度 時,進行擴容

    擴容增量:原容量的 1倍

    如 Vector的容量為10,一次擴容後是容量為20

Set(集) 元素無序的、不可重複。

HashSet:執行緒不安全,存取速度快

     底層實現是一個HashMap(儲存資料),實現Set介面

     預設初始容量為16(為何是16,見下方對HashMap的描述)

     載入因子為0.75:即當 元素個數 超過 容量長度的0.75倍 時,進行擴容

     擴容增量:原容量的 1 倍

      如 HashSet的容量為16,一次擴容後是容量為32

在JDK1.8中我們可以發現其實HashSet 的建立其實就是一個HashMap
這裡寫圖片描述
Map是一個雙列集合

HashMap:預設初始容量為16

     (為何是16:16是2^4,可以提高查詢效率,另外,32=16<<1 –>至於詳細的原因可另行分析,或分析原始碼)

     載入因子為0.75:即當 元素個數 超過 容量長度的0.75倍 時,進行擴容

     擴容增量:原容量的 1 倍