Map
Map接口:雙列數據,保存具有映射關係(key-value)成對的物件
分類
HashMap
:主要實現類,線程不安全、效率高、可以存null
LinkedHashMap
:遍歷時可以按照添加順序排列
TreeMap
:可以按照key
(必須都是同類)來排序,底層是紅黑樹Hashtable
:古老的實現類,注意t是小寫,線程安全、效率低、不能存null
Properties
:常用來處理配置文件,key
跟value
都是String
類型
結構
key
:無序、不可重複的,使用Set
儲存。key
決定存放位置,key
的所在類必須重寫equals()
與hashCode()
value
:無序,可重複的,使用Collection
儲存,value
的所在類必須重寫equals()
- 一對
key
與value
構成一個Entry
物件,使用Set
儲存,當然也是無序、不可重複的
HashMap源碼分析
以JDK7為例
HashMap map = new HashMap();
實例化時,底層創建了一個長度16的一維數組Entry[] table
map.put(key1, value1);
:放數據時,調用key1
所在類的hashcode()
方法算出哈希值,以此哈希值再經過某些位運算,得到在Entry[] table
數組中的存放位置,此時:- 若位置是空的,放入成功
- 若已經有人(可能有一個數據或鏈表),就來比較key1跟佔位者(可能有多個,全都要比一遍)的哈希值
- 如果哈希值都不同,添加成功(放成鏈表)
- 如果
key1
的哈希值跟已存在的某數據(舉例為key2, value2
)哈希值相同,此時再比較key1
所在類的equals()
方法,根據返回值:- 如果
equals()
返回false
,表示key不同只是恰好哈希值一樣,添加成功(放成鏈表) - 如果
equals()
返回true
,表示兩者key真的一樣,那就進入至尊對決,此時把put()
方法理解為覆蓋,將舊的value2
換成新的value1
- 如果
- 所謂放成鏈表跟前面
Set
提到的一樣,類似於"卜"字的概念,從該位置延伸出去存放新的元素
- 擴容:超過臨界值(容量*負載因子)且要存的位置非空,就會進行擴容,預設的擴容方式為造一個新的兩倍長數組,然後將原有的複製過來
- 預設容量:16
- 預設負載因子:0.75
- 擴容的邏輯是這樣的,為了減少哈希碰撞(就是不希望分支的鏈表太多太長),所以不會等他裝到滿才擴容。
- 假如負載因子0.9可能老是撞車導致分支很多,負載因子0.2可能一直在擴容,折衷就定負載因子為0.75效率最高
JDK8的改動
new HashMap()
:實例化時,底層沒有創建數組,首次調用put()
方法才創建,類似懶漢式- 底層數組用
Node[]
取代Entry[]
- 當數組某一個位置上的元素以鏈表形式存在的數據>8,且當前數組長度>64時,將鏈表改為紅黑樹儲存,提高查找效率
- 白話:分支長度>8且主幹長度>64轉紅黑樹
LinkedHashMap
繼承了HashMap
的Node
,但又多了before
跟after
兩個屬性,所以形成雙向鏈表,可以在添加時記錄前後順序
Map常用方法
預設使用一個HashMap的實例物件作為調用的主體演示
Object put(Object key, Object value)
:添加或修改,並且返回null
或被取代的value
void putAll(Map m)
:將m所有的對都放進去Object remove(Object key)
:移除指定key的鍵值對,返回被刪除的valuevoid clear()
:清空當前map中所有數據,注意與map=null
不同,只是清空數據,本身還在Object get(Object key)
:獲取對應的valueboolean containsKey(Object key)
:是否有此keyboolean containsValue(Object value)
:是否有此valueint size()
:返回鍵值對個數boolean isEmpty()
:是否size=0boolean equals(Object key)
:判斷當前map與obj是否相等
遍歷Map
切出Collection的部分再用Iterator來遍歷
-
Set keySet()
:返回所有key構成的Set集合 -
Collection values()
:返回所有value構成的Collection集合 -
Set entrySet()
:返回所有key-value構成的Set集合,得到的元素每個都是entry,故也能強轉回去Map再進行一些操作,舉例:HashMap map = new HashMap(); map.put("A", 123); map.put("B", 1234); map.put("c", 14564); Set entrySet = map.entrySet(); Iterator iterator = entrySet.iterator(); while (iterator.hasNext()) { Object obj = iterator.next(); Map.Entry entry = (Map.Entry) obj; System.out.println(entry.getKey() + "=>" + entry.getValue()); }
TreeMap
實際發開中非常少用,了解即可
規定了key
必須都是同類,單看key
其實就是TreeSet
,排序的規則都是一樣的必須重寫compareTo()
或compare()
方法
-
自然排序:加入的類若實現了
Comparable
接口,此時比較兩個物件是用重寫的compareTo()
方法而非equals()
,要注意自己重寫的比較邏輯,否則可能會產生兩個物件被誤認為重複元素的狀況 -
訂製排序:用自訂的比較器,此時比較兩個物件是用重寫的
compare()
方法而非equals()
,舉例:
Properties
- key跟value都是String類型,常用於配置文件
- 有
load(文件流)
與getProperty(String)
方法,專門用來讀取配置文件的訊息
Collections工具類
- 雖然叫做Collections,但他能操作Set、List還有Map等集合
- 既然是工具類,顯然都是靜態方法
常用方法
-
reverse(List)
:反轉List中元素的順序 -
shuffle(List)
:隨機排序List中的元素 -
sort(List)
:按照元素自然排序 -
sort(List, Comparator)
:依照指定的Comparator對元素排序 -
swap(List, int, int)
:交換索引位置 -
Object max(Collection)
:根據自然排序返回最大值;也有min方法 -
Object max(Collection, Comparator)
:根據指定的Comparator返回最大值 -
int frequency(Collection, Object)
:返回Object出現次數 -
void copy(List dest, List src)
:將scr的內容複製到dest中,要注意長度,想被貼上的list長度不能比原來的短,可以新建一個填滿null的等長數組去接,舉例:List list = new ArrayList(); // 對list塞了一些元素後,想作為src複製到新的dest List dest = Arrays.asList(new Object[list.size()]); Collections.copy(dest,list);
-
boolean replaceAll(List list, 舊, 新)
:全替換 -
synchronizedXXX(xxx)
:返回一個線程安全的xxx集合
小結
Map
:由Set
存成key
(無序不可重複),由Collection
存成value
(無序可重複),key
與value
組合成無序不可重複的Entry
或說Node
結構HashMap
的底層原理:16位長的數組,put
元素時拿key
跑hashCode()
再位移運算得出要放的位置,若已經有東西,再比key
的euqals()
方法,若false表示哈希值恰好相同,在此處分支成鏈表結構儲存;若euqals()
返回true
表示key
完全相等,則進行覆蓋的操作Hashtable
:古老、線程安全、效率低,過時不用;但其下有Properties
常用來存配置,特色是K-V都是String
HashTree
:key
的部分就是SetTree
,要求key
都是同類,重寫compareTo()
或compare()
方法後可以用key
排序- 遍歷
Map
,用keySet
方法取出key
為一個Set
就能用Iterator
遍歷了,還可以用這個key
調用get
方法獲取對應的value
執行各種操作 Collection
是一個抽象接口,其下有List
跟Set
與對應的實現類用來存取單列數據Collections
是一個工具類,有很多靜態方法去操作List
、Set
還有Map
上次修改於 2021-12-09