JAVA學習筆記——集合

比較經常使用的集合通常有4種接口,每種接口能夠由多種數據結構去實現。這樣咱們就能夠在實現相同接口(功能)的狀況下,根據具體場景選擇不一樣的數據結構。java

Interface Hash Table Resizable Array Balanced Tree Linked List Hash Table + Linked List
Set HashSet TreeSet LinkedHashSet
List ArrayList LinkedList
Deque ArrayDeque LinkedList
Map HashMap TreeMap LinkedHashMap

1. List

ArrayList、LinkedList、Vector都實現了接口List。List接口是一個有順序的集合,元素能夠重複,能夠經過索引去訪問元素。api

1.1 ArrayList

ArrayList類維護了一個數組,訪問操做效率高,刪除和插入操做效率低。數組

transient Object[] elementData; // non-private to simplify nested class access

當增長元素且ArrayList的容量不夠時,須要對數組進行擴容,擴容使用Arrays.copyOf()方法。
當刪除元素時,會使用System.arraycopy()方法。
以上兩個copy方法都是蠻費時間的。緩存

1.2 LinkedList

LinkedList類維護了一個雙端鏈表,first和last分別指向鏈表的頭尾Node。安全

1.3 Vector

Vector類與ArrayList類很是相像,主要的區別是:Vector是線程安全的,ArratList不是線程安全的。數據結構

protected Object[] elementData;

1.3.1 Stack

Stack類繼承了Vector類,由於Vector類維護了一個數組,在數組基礎上實現LIFO的功能仍是很方便的。但這也會帶來2個問題:oracle

  1. Stack類是基於數組實現的,push和pop操做的開銷會比較大。
  2. Stack可使用Vector設置任意位置元素的方法setElementAt(E obj, int index),那麼Stack就不是純粹的棧,棧只能操做棧頂的元素。

JDK文檔建議,若是要使用棧這種數據結構,應該優先使用Deque<Integer> stack = new ArrayDeque<Integer>();優化

2. Deque

Deque是Double End Queue的意思,該雙端隊列便可以用做FIFO的隊列,也能夠用做LIFO的棧。Stack類也是棧,但它有些缺點,應該優先使用Deque做爲棧。線程

2.1 LinkedList

LinkedList類維護了一個雙端鏈表,first和last分別指向鏈表的頭尾Node。code

2.2 ArrayDeque

ArrayDeque是在JDK1.6中才出現的。以數組的形式實現雙端隊列。
JDK文檔建議:

  1. 當要使用棧時,ArrayDeque比Stack要高效;
  2. 當要使用隊列時,ArrayDeque要比LinkedList要高效。

第1點的緣由在上面說過解釋過,第2點的緣由有如下兩點:

  1. LinkedList每次push一次須要new一個Node,每次pop一次會產生一個Node垃圾等待回收。ArrayDeque因爲是數組,push不會new,pop不會產生垃圾,雖然在容量不夠時,仍是會作擴大一倍容量的操做。
  2. LinkedList在內存中是分散的,因此緩存命中率會比較低。

3. Map

3.1 HashMap

HashMap(散列表)是一個增刪、查找都高效的數據結構。查找散列表通常分爲2個步驟:

  1. 計算鍵的hash值;
  2. 找到相應的位置並解決衝突,放入該鍵值對。
    解決衝突有2種方法:拉鍊法和開放地址法(線性探測、二次探測、隨機探測)

java中的HashMap採用的是拉鍊法,這是數組和鏈表的結合形式。

HashMap中的put()方法:

  1. 計算哈希值。經過hashcode()計算出該對象key的hash值
  2. 哈希值取模。對hash值進行取模操做hash%length,length是數組的長度。該長度應該是2的冪方,這樣咱們就能夠對取模操做進行優化hash&(length-1)
  3. 插入鏈表。獲得模值以後,就將其做爲索引找到相應的鏈表表頭,該條鏈表中存儲hash值同樣的鍵值對,鍵值對存儲在靜態內部類Entry中。Entry保存鍵、值以及指向下一個鍵值對的next。

HashMap中的resize()方法:
當entry的數量大於閾值時,開始resize()。其中閾值是容量乘以加載因子。
resize()以後,table長度變爲2倍,從新取模,從新鏈表插入。

負載因子:當hashmap中的有效容量除以總容量大於負載因子時,hashmap的容量就會翻倍。java的hashmap默認容量是16,負載因子時0.75。
容量:

Java8對HashMap的改進:當鏈表的長度大於必定值時,鏈表就轉換爲紅黑樹,實現了快速的增刪改查,時間複雜度從順序查找的O(n)優化到O(logn)。

HashTable與HashMap很是類似,他們有如下三點區別:

  1. HashTable是synchronized的,是線程安全的,多個線程能夠共享;HashMap相反
  2. HashTable是synchronized的,在單線程下速度會比較慢;HashMap的速度比較快
  3. HashTable不能接受null的鍵和值;HashMap則能夠

【Reference】

  1. JDK API文檔 http://docs.oracle.com/javase/8/docs/api/
相關文章
相關標籤/搜索