你們都知道在JAVA中若是咱們要存儲和樹立一組同類型的數據的時候,咱們通常都採用數組來存儲。可是你們知道數組一旦被建立,其長度就固定不變了,因此使用數組的時候須要知道或者說是咱們要估算一下數據的規模,以方便咱們建立長度適合的數組。若是咱們估計的長度比實際須要的長度大,那則會浪費存儲空間;若比實際長度小,則處理數據時會遇到麻煩,所以,用數據存儲數目不肯定的元素那樣是一個不明智的選擇。算法
ArrayList 和Vector是採起數組體式格式存儲數據,此數組元素數大於實際存儲的數據以便增加和插入元素,都允許直接序號索引元素,然則插入數據要設計到數組元素移動等內存操縱,因此索引數據快插入數據慢,Vector由於應用了synchronized辦法(線程安然)因此機能上比ArrayList要差,LinkedList應用雙向鏈表實現存儲,按序號索引數據需要進行向前或向後遍歷,然則插入數據時只需要記錄本項的先後項便可,因此插入數度較快!Collection
├List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack數組
├Set
│├HashSet
│├LinkedHashSet
│└TreeSet安全
Map
├Hashtable
├HashMap
├LinkedHashMap
├LinkedHashMap
└TreeMap數據結構
Collection接口
Collection是最根蒂根基的湊集接口,一個Collection表明一組Object,即Collection的元素(Elements)。一些Collection允許雷同的元素而另外一些不可。一些能排序而另外一些不可。Java SDK不供給直接持續自Collection的類,Java SDK供給的類都是持續自Collection的「子接口」如List和Set。
全部實現Collection接口的類都必須供給兩個標準的機關函數:無參數的機關函數用於建立一個空的Collection,有一個Collection參數的機關函數用於建立一個新的Collection,這個新的Collection與傳入的Collection有雷同的元素。後一個機關函數允許用戶複製一個Collection。
如何遍歷Collection中的每個元素?非論Collection的實際類型如何,它都支撐一個iterator()的辦法,該辦法返回一個迭代子,應用該迭代子便可一一接見Collection中每個元素。典範的用法以下:
Iterator it = collection.iterator(); // 得到一個迭代子
while(it.hasNext()) {
Object obj = it.next(); // 得到下一個元素
}
由Collection接口派生的兩個接口是List和Set。併發
List接口
List是有序的Collection,應用此接口能夠或許正確的把握每一個元素插入的地位。用戶能夠或許應用索引(元素在List中的地位,相似於數組下標)來接見List中的元素,這相似於Java的數組。
和下面要提到的Set不合,List允許有雷同的元素。
除了具備Collection接口必備的iterator()辦法外,List還供給一個listIterator()辦法,返回一個ListIterator接口,和標準的Iterator接口比擬,ListIterator多了一些add()之類的辦法,允許添加,刪除,設定元素,還能向前或向後遍歷。
實現List接口的經常使用類有LinkedList,ArrayList,Vector和Stack。ide
LinkedList類
LinkedList實現了List接口,允許null元素,雙向循環鏈表的數據結構,因爲是基於鏈表的,因此沒法法實現隨機訪問的,只能順序訪問,是不一樣步的,增刪元素的速度很快。LinkedList可被用做堆棧(stack),隊列(queue)或雙向隊列(deque)。函數
ArrayList類
內部是數組數據結構,是不一樣步的,查詢的速度快。它允許全部元素,包含null。ArrayList沒有同步。
每一個ArrayList實例都有一個容量(Capacity),即用於存儲元素的數組的大小,默認是10。這個容量可跟着絡續添加新元素而主動增加,容量不夠時默認增長兩倍。若是指定容量,容量必須大於當前容量的三倍時才生效。當需要插入多量元素時,在插入前能夠調用ensureCapacity辦法來增加ArrayList的容量以進步插入效力。性能
Vector類
Vector非常相似ArrayList,然則Vector是同步的。默認容量是10,容量增長策略默認是增長一倍,若是指定容量則必須大於當前容量的兩倍。線程
Stack 類
Stack持續自Vector,Stack 類表示後進先出(LIFO)的對象堆棧,Stack剛建立後是空棧。設計
List:
|--Vector:內部是數組數據結構,是同步的。增刪,查詢都很慢!
|--ArrayList:內部是數組數據結構,是不一樣步的。替代了Vector。查詢的速度快。
|--LinkedList:內部是鏈表數據結構,是不一樣步的。增刪元素的速度很快。
若是常常對數組作隨機插入操做,特別是插入的比較靠前,那麼LinkedList的性能優點就很是明顯,而若是都只是末尾插入,則ArrayList更佔據優點,若是須要線程安全,則使用Vector或者建立線程安全的ArrayList。在使用基於數組實現的ArrayList 和Vector 時咱們要指定初始容量,由於咱們在源碼中也看到了,在添加時首先要進行容量的判斷,若是容量不夠則要建立新數組,還要將原來數組中的數據複製到新數組中,這個過程會減低效率而且會浪費資源。
Set接口
Set是一種無序的不包含重複元素的Collection,即隨便率性的兩個元素e1和e2都有e1.equals(e2)=false,Set最多有一個null元素。
HashSet類
HashSet中存放的元素是無序的,底層是用HashMap實現的,其中key是要放入的元素,value是一個Object類型的名爲PRESENT的常量,因爲用到了散列函數,所以其存取速度是很是快的,在地址空間很大的狀況下它的存取速度能夠達到O(1)級。
LinkedHashSet
LinkedHashSet低層是LinkedHashMap,將數據按存儲順序或訪問順序排序。
TreeSet
TreeSet低層是TreeMap,將數據天然排序或按key排序。
Map接口
Map沒有持續Collection接口,Map供給key到value的映射。一個Map中不能包含雷同的key,每一個key只能映射一個value。Map接口供給3種湊集的視圖,Map的內容能夠被算做一組key湊集,一組value湊集,或者一組key-value映射。
HashMap
HashMap是一種基於哈希表(hash table)實現的map,哈希表(也叫關聯數組)一種通用的數據結構,由數組加鏈表實現,其概念也比較簡單:key通過hash函數做用後獲得一個槽(buckets或slots)的索引(index),槽中保存着咱們想要獲取的值,以下圖所示
線程非安全,而且容許key與value都爲null值,不保證其內部元素的順序,並且隨着時間的推移,同一元素的位置也可能改變(resize的狀況)
put、get操做的時間複雜度爲O(1)。遍歷其集合視角的時間複雜度與其容量(capacity,槽的個數)和現有元素的大小(entry的個數)成正比,因此若是遍歷的性能要求很高,不要把capactiy設置的太高或把平衡因子(load factor,當entry數大於capacity*loadFactor時,會進行resize,reside會致使key進行rehash)設置的太低。
HashMap的桶數目,即Entry[] table數組的長度,因爲數組是內存中連續的存儲單元,它的空間代價是很大的,可是它的隨機存取的速度是Java集合中最快的。咱們增大桶的數量,而減小Entry<Key,Value>鏈表的長度,來提升從HashMap中讀取數據的速度。這是典型的拿空間換時間的策略。可是咱們不能剛開始就給HashMap分配過多的桶(即Entry[] table 數組起始不能太大),這是由於數組是連續的內存空間,它的建立代價很大,何況咱們不能肯定給HashMap分配這麼大的空間,它實際到底可以用多少,爲了解決這一個問題,HashMap採用了根據實際的狀況,動態地分配桶的數量。
HashMap的權衡策略
要動態分配桶的數量,這就要求要有一個權衡的策略了,HashMap的權衡策略是這樣的:
若是HashMap的大小> HashMap的容量(即Entry[] table的大小)*加載因子(經驗值0.75),則HashMap中的Entry[] table 的容量擴充爲當前的一倍;而後從新將之前桶中的Entry<Key,Value>鏈表從新分配到各個桶中。上述的 HashMap的容量(即Entry[] table的大小) * 加載因子(經驗值0.75)就是所謂的閥值(threshold):
閥值(threshold)=容量(capacity)*加載因子(load factor)
容量(capacity):是指HashMap內部Entry[] table線性數組的長度,默認是16
加載因子(load factor):默認爲0.75
閥值(threshold):當HashMap大小超過了閥值,HashMap將擴充2倍,而且rehash。
put()方法-向HashMap存儲鍵值對<Key,Value>
a. 獲取這個Key的hashcode值,根據此值肯定應該將這一對鍵值對存放在哪個桶中,即肯定要存放桶的索引;
b. 遍歷所在桶中的Entry<Key,Value>鏈表,查找其中是否已經有了以Key值爲Key存儲的Entry<Key,Value>對象,
c1. 若已存在,定位到對應的Entry<Key,Value>,其中的Value值更新爲新的Value值;返回舊值;
c2. 若不存在,則根據鍵值對<Key,Value> 建立一個新的Entry<Key,Value>對象,而後添加到這個桶的Entry<Key,Value>鏈表的頭部。
d. 當前的HashMap的大小(即Entry<key,Value>節點的數目)是否超過了閥值,若超過了閥值(threshold),則增大HashMap的容量(即Entry[] table 的大小),而且從新組織內部各個Entry<Key,Value>排列。
HashMap在肯定Key是否在HashMap中存在的要求有兩個:
1. Key值是否相等;
2. hashcode是否相等;
因此咱們在定義類時,若是重寫了equals()方法,可是hashcode卻沒有保證相等,就會致使當使用該類實例做爲Key值放入HashMap中,會出現HashMap「工做異常」的問題,會出現你不但願的狀況。
LinkedHashMap
LinkedHashMap是HashMap的一個子類,它保留插入的順序,若是須要輸出的順序和輸入時的相同,那麼就選用LinkedHashMap。
LinkedHashMap是Map接口的哈希表和連接列表實現,具備可預知的迭代順序。此實現提供全部可選的映射操做,並容許使用null值和null鍵。此類不保證映射的順序,特別是它不保證該順序恆久不變。
LinkedHashMap實現與HashMap的不一樣之處在於,後者維護着一個運行於全部條目的雙重連接列表。此連接列表定義了迭代順序,該迭代順序能夠是插入順序或者是訪問順序。
其實LinkedHashMap幾乎和HashMap同樣,不一樣的是它定義了一個Entry<K,V> header,這個header不是放在Table裏,它是額外獨立出來的。LinkedHashMap經過繼承hashMap中的Entry<K,V>,並添加兩個屬性Entry<K,V> before,after,和header結合起來組成一個雙向鏈表,來實現按插入順序或訪問順序排序。
TreeMap
TreeMap的實現是紅黑樹算法的實現,TreeMap中全部的元素都保持着某種固定的順序,若是你須要獲得一個有序的結果你就應該使用TreeMap。
HashTable
Hashtable幾乎能夠等價於HashMap,除了HashMap是非synchronized的,並能夠接受null(HashMap能夠接受爲null的鍵值(key)和值(value),而Hashtable則不行)。
CurrentHashMap
Hashtable的synchronized是針對整張Hash表的,即每次鎖住整張表讓線程獨佔,ConcurrentHashMap容許多個修改操做併發進行,其關鍵在於使用了鎖分離技術。它使用了多個鎖來控制對hash表的不一樣部分進行的修改。ConcurrentHashMap內部使用段(Segment)來表示這些不一樣的部分,每一個段其實就是一個小的hash table,它們有本身的鎖。只要多個修改操做發生在不一樣的段上,它們就能夠併發進行。