我所理解的JDK集合類(一):基本介紹

在平常開發中,集合類是常常使用的工具,是JDK根據基礎的java代碼經過java語言來進行的一些封裝。Java Collection由兩套並行的接口組成,一套是Collection接口,一套是Map接口。Collection接口的子接口及實現類有List、Set、Stack、Queue等。java

  List接口的特色是有序可重複。實現類一般使用的是ArrayList和LinkedList。數組

  Set接口的特色是不可重複。實現類一般使用的是HashSet、LinkedHashSet和TreeSet。安全

  Stack類的特色是後進先出,內部經過繼承Vector來實現。併發

  Queue接口的特色是先進先出,是中間件開發經常使用的類。函數

  Map接口的特色是存儲鍵值對,鍵不可重複。實現類一般使用的是HashMap、LinkedHashMap和TreeMap。工具

  ArrayList底層由數組實現,數組的默認初始大小size爲10,擴容係數爲0.5。在構造函數中能夠指定size。ArrayList特色是能夠根據數組索引快速定位到元素。 擴容的源碼:優化

int newCapacity = oldCapacity + (oldCapacity >> 1);  //  >>位移運算符,快速計算 /2的大小。
...  // 省略其餘
elementData = Arrays.copyOf(elementData, newCapacity); // Arrays.copyOf()底層是native的數組複製,快速。

  LinkedList底層由雙向鏈表實現,特色是增長元素時能夠快速添加。刪除元素時,定位元素比較慢,定位到以後能夠快速刪除。線程

  ArrayList和LinkedList是線程不安全的,推薦在方法內使用。Vector是線程安全版的ArrayList。設計

  Set系列的實現是經過組合對應的Map實現類來實現的,Set存儲的元素做爲Map的key,value = new Object();code

  HashMap底層由hash表+鏈表+數組實現的。JDK1.8以後HashMap的底層增長了紅黑樹結構。數組的默認初始大小size爲16,擴容係數factor爲2。在構造函數中能夠指定size和factor。HashMap的特色是查詢快速,是典型的以空間換時間的作法。缺點是擴容時很耗時。

  LinkedHashMap是經過繼承HashMap,重寫entry內部類中增長雙向鏈表來實現的。經過雙向鏈表來表示元素的存取順序。

  TreeMap的底層是經過紅黑樹來實現的。

  HashMap、LinkedHashMap和TreeMap是線程不安全的,推薦在方法內使用。HashTable是線程安全版的HashMap,可是不支持null做爲key。

接下來簡單介紹一下HashMap的原理
  put(k,v)時,先根據hashCode和equels判斷是否已經存在相同的key,若是存在則返回舊的value並保存新的value。存儲key時,先根據k.hashCode()獲得hash值,再經過HahsMap本身實現的hash()等到更加均勻散列的hash值(JDK1.8去掉了此步驟),經過hash值對數組的長度求餘來肯定該key應該存在的位置,其實就是獲取hash值對應的數組索引。若是當前位置有值(說明發生了hash碰撞),則經過鏈表來增長該元素。最後再判斷是否須要進行擴容resize()。

  get(k)時,根據k.hashCode()計算獲得數組索引,再遍歷鏈表判斷hashCode和equels肯定已存儲的key來返回對應的value,若是沒有則返回null。

  根據hash值求餘計算數組的索引,源碼中採用的是位移運算 hash & (table.length -1),這也就是初始數組的大小會是2的n次方以及擴容係數是2的緣由。而在構造中保證數組大小是不小於傳參的最小的2的n次方實現,也是經過位移運算(>>)來實現的。經過位移運算來保證運算速度,設計精巧。

HashMap的擴容resize()。
  每一次put(k,v)元素以後,都須要進行判斷是否進行擴容。噹噹前存儲的元素的個數 size > table.length * factor 的時候,就須要進行擴容。擴容很耗時,須要創建新的數組,而後遍歷就數組的每個元素並從新計算對應的數組索引並複製存儲。因此使用HashMap的優雅方式是在構造參數中指定數組的大小(提早預估),即便是默認的16。這是優化HashMap的一個方法。HashMap在併發擴容的時候可能會發生死循環

  JDK1.8的HashMap中引入了紅黑樹結構,在put(k,v)的時候,當鏈表的節點數≥8,則自動轉化爲紅黑樹結構存儲,目的是減小在極端狀況下hash衝突致使都存儲在同一個鏈表上時查詢過慢的狀況。由於鏈表查找的時間複雜度爲O(n),而紅黑樹則是O(logn)。

  HashMap容許存儲key爲null的狀況。內部實現是經過將null做爲特殊的key判斷,存儲在數組的第一個元素中。

ConcurrentModificationException
  List、Set在進行foreach遍歷的時候,是不容許經過list.remove(e)等操做來增刪元素的,不然回報ConcurrentModificationException。foreach是經過iterator的.hasNext()和.next()來實現的。List有個modCount的屬性來記錄當前list被操做的次數,在add(e)、remove(e)、clear()等操做的時候都會觸發modCount++。Iterator內部有expectedModCount屬性,在構造Iterator的時候會expectedModCount = modCount。當使用iterator.remove()的時候,會調用list.remove(e)以及強制expectedModCount = modCount。每次iterator.next()的時候都會判斷expectedModCount和modCount是否相等。若是不相等則表示被不正確的修改過,而後 throw new ConcurrentModificationException() 。若是要在遍歷期間修改元素,請使用iterator.remove()或者listIterator.add(e)。

相關文章
相關標籤/搜索