1.爲什麼要使用集合
1.在開發中我們經常要集中保存多個數據,可以使用數組,但是使用數組的前提是我們必須知道我們要保存的數據的數量,數組 的長度一旦被定義就不可以再做改變
2.java集合相當於是一個長度可以變化的數組,是用於保存動態增長的數據的一個很好的選擇
3.java集合因爲是用來保存數據的,所以我們也稱集合類爲容器類,所有的集合類都位於java.util包下
4.後來爲了處理多線程問題,JDK1.5中還在java.util.concurrent包下提供了支持多線程的java集合類
2.Collection和Map
- Collection ---一組對立的元素,這些元素都必須符合一些規則---
- List 必須保持元素特定的順序
- Set 不能有重複的元素
- Queue 保持一個隊列的順序(即先進先出)
- Map ---一組成對的鍵值對對象---
- Collection和Map的區別在於容器中每個位置保存的元素個數
- Collection每個位置只能保存一個元素(對象)
- Map的每個位置保存的是一個鍵值對對象,每個鍵值對對象由一個key和一個value組成,我們可以通過key來尋找value
3.集合類的框架層次關係
Collection
Collection是最基本的集合接口,一個collection代表一組object對象,這些object是collection中的元素(這個接口不能實例化對象,只能用來定義規範)
-
Set
- Set繼承自Collection接口,Set就像是一個罐子,其中的元素沒有順序,但是元素不可以重複(整個Set類層次的共有屬性)
- Set在判斷兩個元素是否相同時,使用的是equals()方法而不是==,說明Set判斷的是兩個元素的值是否相同
- 向Set中添加新元素時,會先將新元素跟其他所有元素進行equals()比較,返回值都爲false時,Set纔會接受這個新元素
HashSet
HashSet實現了Set接口,使用HashSet時,第一件事就是
- 重寫hashCode()和equals()方法,這樣才能確保不會出現重複的元素(要保證這兩個方法兼容)
重溫一下hash碼(散列碼)的知識:
- hashCode是由對象導出的一個整數值,每一個對象都有一個默認的散列碼,值就是對象的內存地址(將內存地址換算成一個整數)。(也有一些類的對象的散列碼是另外經過計算的,例如String)。
- HashSet的add機制 假如我們有一個散列碼是76268,而此時的HashSet有128個散列單元,那麼這個數據很有可能會插入數組的108個散列鏈表中(76268%128=108)。但這只是可能,如果第108個散列鏈表中經過equals比較爲true的話,這個新元素將會被丟棄,不再加入散列鏈表中
HashSet的底層是由HashMap實現的,元素都存儲在HashMap鍵值對的KEY上,而value值是一個統一的值private static final Object PRESENT = new Object();
HashSet的操作實際上都是使用HashMap的底層方法來完成的 ,所以.......請好好理解HashMap的底層實現原理
HashSet是無序的,它也沒有提供get()方法,所以只能通過迭代來得到想要的元素,但是因爲能夠通過散列碼%散列單元數,就能夠快速找到存儲這個元素的散列鏈表,所以HashSet的查找效率得到提升
加入新元素時,會先比較hash碼值,再進行equals比較(兩個不同對象的hash碼值有可能會相同,因爲hashCode()方法可能會被重寫,不一定都是使用Object的hashCode()方法)
- hash碼值不相同,說明是一個新元素,存入
- hash碼值相同,再進行equals比較,如果返回true,說明已經存在,不存
- hash碼值相同,再進行equals比較,如果返回false,說明不存在,存入
LinkedHashSet
LinkedHashSet也是根據元素的hashCode來決定存儲元素的位置,但是與HashSet不同的是,linkedHashSet同時會使用鏈表來維護元素的次序,這樣使得元素看起來是以插入順序來存儲的,因爲要維護插入順序,所以性能要低於HashSet
-
List
- List代表一個元素有序,且可以重複的集合,集合中每個元素都有其對應的元素索引
- 可以通過索引來訪問特定位置的元素
- 默認按照元素的添加順序來設定索引
ArrayList
繼承自AbstractList類,實現了List、RondomAccess(快速訪問)、Cloneable(複製)、Serializable(序列化)接口
底層基於實現了大小能動態變化,能重新分配的Object數組,允許null存在,默認長度10
數組的存儲地址是連續的
底層實現的源碼https://blog.csdn.net/zxt0601/article/details/77281231
Vector
- (與ArrayList的相同點)和ArrayList繼承了相同的類,實現了相同的接口,底層實現也是動態數組,默認長度也是10
- 不同點
- Vector的方法都是同步的(Synynchronized),線程安全(thread-safe)的,ArrayList不是,但是線程同步就必定會影響性能,所以ArrayList的性能要優於Vector
- 當Vector和ArrayList的元素大小超過初始大小時,Vector會將容量翻倍,但是ArrayList值增加50%,因此ArrayList更節省空間大小
LinkedList
繼承自AbstractSequentialList的雙向鏈表,實現了List、Deque(隊列)、Cloneable(複製)、Serializable(序列化)接口
鏈表的存儲空間是不連續的
ArrayList和LinkedList的區別
- ArrayList是基於動態數組(順序表)的數據結構,順序表的存儲地址是連續的,所以在查找的時候比較快,但是在插入和刪除的時候,由於要把其他元素順序向後或者向前移動,所以比較費時
- LinkedList是基於鏈表的數據結構,鏈表的存儲地址是不連續的,每個存儲地址通過指針指向,在查找的時候需要通過指針進行元素遍歷,所以在查找的時候比較費時。由於鏈表插入時不需要移動其他元素,所以適合插入和刪除
Map
Map是由一系列鍵值對組成的集合,KEY-VALUE,Map中的key不能相同,value值可以相同
從代碼複用的角度來看,Java先是實現了Map,然後通過包裝了一個所有value都是null的Map,就形成了Set
HashMap和Hashtable的區別
HashMap和Hashtable的關係類似於ArrayList和Vector
區別:
- Hashtable是線程安全的,HashMap是線程不安全的,所以性能上HashMap要優於Hashtable
- Hashtable不允許使用nul作爲key和value的值,但是HashMap可以