十二、集合:Map
1.Map集合繼承結構圖:【UML】
圖片來源網路,如有侵權私信刪除。
2.Map介面:
2.1、Map與Collection沒有繼承關係。
2.2、Map集合以key和 value的方式儲存資料型別:鍵值對
key和value都是引用資料型別。儲存的都是物件的記憶體地址。
key起到主導作用,value是key的附屬品。
2.3、常用方法:
V put(K key, V value); //向Map集合新增鍵值對
V get(Object key); //通過key獲取value
void clear(); //清空Map集合
boolean containsKey(Object key); //判斷Map集合中包含某個key
boolean containsValue(Object value); //判斷Map集合中包含某個value
boolean isEmpty(); //判斷Map集合中元素個數是否為0
Set<K> keySet(); //獲取Map集合所有的key(所有的鍵是一個set集合)
V remove(Object key); //通過key刪除鍵值對
int size(); //獲取Map集合中鍵值對個數
Collection<V> values(); //獲取Map集合中所有的value,返回一個Collection
Set<Map.Entry<K, V>> entrySet(); //將Map集合轉換成Set集合
Map集合通過entrySet()方法轉換成這個Set集合,Set集合中元素型別是 【Map.Entry<K, V>】
Map.Entry與String一樣,是一種型別的名字。只不過,Map.Entry是靜態內部類,是Map中的靜態內部類。
View Code2.5、Map集合的遍歷:
第一種:獲取所有的key,通過遍歷key,來遍歷value
第二種:Set<Map.Entry<k,v>> entrySet();
獲取key和value都是直接從node物件中獲取屬性值(適合大資料量)
View Code3.HashMap集合:
3.1、1.HashMap集合底層是雜湊表/散列表的資料結構。
3.2、雜湊表的資料結構:
雜湊表是一個數組和單向連結串列的結合體。
陣列:在查詢方面效率很高,隨機增刪效率很低。
連結串列:在隨機增刪方面效率較高,在查詢方面效率很低。
雜湊表將以上兩種資料結構融合在一起,發揮各自的有點。
3.3、HashMap集合底層的原始碼:
public class HashMap{ //HashMap底層是一維陣列 transient Node<k,v>[] table; //靜態內部類 static class Node<K,V> implements Map.Entry<K,V> { final int hash;//雜湊值(雜湊值是key的hashCode()方法執行的結果) //hash值通過雜湊函式/演算法,可以轉換儲存陣列的下標。 final K key; V value; Node<K,V> next; } }View Code
雜湊表/散列表:一維陣列,這個陣列中每一個元素是一個單項鍊表(陣列和連結串列的結合體)。
3.4、以下方法的實現原理:
public V put(K key, V value);
原理:
第一步:先將k,v分裝到Node物件當中。
第二步:底層呼叫k的hashCode()方法得出hash值然後通過雜湊函式/演算法,將hash值轉換成陣列的下標,下標位置上如果沒有任何元素,就把Node新增到那個位置上。如果下標對應的位置上有連結串列,此時會拿著 k 和連結串列上的每個節點的k進行equals,如果所有的equals方法返回的都是false,那麼這個節點將會被新增到連結串列的末尾,如果其中有一個equals返回了true,這個節點的value將會被覆蓋。
public V get(Object key);
原理:
先呼叫k的hashCode()方法得出雜湊值,通過雜湊演算法轉換成陣列下標,通過陣列下標快速定位到某個位置上,如果這個位置什麼都沒有返回null。如果這個位置有單向連結串列,則會拿著引數k和單向連結串列上的每一個節點中的k進行equals,如果所有equal方法返回false,那麼get方法返回null。如果其中一個equals方法返回true,那這個節點的value就是要找的value,get方法最終返回要找的value。
重點:
HashMap集合的key,會先後呼叫兩個方法,一個方法是hashCode(),一個是equals(),這兩個方法都需要重寫。
雜湊表的資料結構:
圖片來源網路,如有侵權私信刪除。
3.5、為什麼雜湊表的隨機增刪,以及查詢效率都很高?
增刪是在連結串列上完成的。查詢也不需要都掃描,只需要部分掃描。
3.6、HashMap集合key部分特點:
無序不可重複。
為什麼無序?因為不一定掛在哪個單向連結串列上。
不可重複怎麼保證?equals方法確保HashMap集合的key不可重複。k重複則覆蓋value。
放在HashMap集合key部分的元素其實就是放在HashSet集合中了。
所以HashSet集合中的元素也需要同時重寫hashCode() + equals() 方法。
3.7、注意:
同一個單向連結串列上所有節點的hash相同,因為陣列下標相同。
但同一個連結串列上k和k的equals方法肯定返回的是false。
雜湊表HashMap使用不當時無法發揮性能。
假設所有的hashCode()方法返回值固定為某個值,將會導致底層雜湊表變成純單向連結串列。
這種情況我們稱為:雜湊分佈不均勻。
假設所有的hashCode()方法返回值都設定為不一樣的值,將會導致底層雜湊表變成一維陣列
沒有連結串列概念,雜湊分佈不均勻。
雜湊分佈均勻需要重寫hashCode()方法有一定的技巧。
3.8、放在HashMap集合key部分的元素,以及放在HashSet集合的元素,需要同時重寫hashCode和equals方法
3.9、HashMap集合預設初始化容量為16,預設載入因子是0.75
這個預設載入因子是當HashMap集合底層陣列的容量達到75%的時候,陣列開始擴容。
重點:HashMap集合初始容量是2的倍數,官方推薦。
這是因為達到雜湊均勻,為了提高HashMap集合的存取效率。
3.10、擴容之後是原容量的2倍。
3.11、HashMap集合key部分允許為null。null只能有一個。
public class Test { public static void main(String[] args) { //測試HashMap集合key元素特點:無序不可重複 //Integer是key,它的hashCode和equals方法都重寫了 Map<Integer , String> map = new HashMap<>(); map.put(121, "aa"); map.put(134, "bb"); map.put(141, "cc"); map.put(151, "dd"); map.put(151, "dd"); System.out.println(map.size());//4 //遍歷 Set<Map.Entry<Integer,String>> set = map.entrySet(); for(Map.Entry<Integer, String> se : set) { System.out.println(se.getKey()+ "=" +se.getValue() ); } Map ma = new HashMap(); ma.put(null, "null"); System.out.println(ma.size()); ma.put(null, "abc"); System.out.println(ma.get(null)); } }View Code
3.12、Map集合先呼叫hashCode(),然後可能呼叫equals();
因為連結串列中元素可能只有一個。
3.13、重寫equals()和hashCode()方法
如果一個equals方法返回true,hashCode()方法返回值必須一樣。equals方法返回true,表示兩個物件相同,在同一個單向連結串列比較,同一個單向連結串列上,雜湊值相同,故hashCode()方法返回值必須相同。
雜湊碰撞(同一連結串列雜湊值可以不同)但轉化下標相同
hashCode()方法和equals()方法用IDE工具生成,但是兩個方法必須同時生成。
3.14、在JDK8之後,如果雜湊表中元素超過8個,單向連結串列這種資料結構會變成紅黑樹資料結構。當紅黑樹上的節點數量小於6時,會重新把紅黑樹變成單向連結串列資料結構。提高檢索效率。
class Student{ String name; public Student() { } public Student(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Student other = (Student) obj; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } } public class Test { public static void main(String[] args) { Student s1 = new Student("aa"); Student s2 = new Student("aa"); System.out.println(s1.equals(s2)); System.out.println(s1.hashCode()); System.out.println(s2.hashCode()); Set<Student> stu = new HashSet<Student>(); stu.add(s1); stu.add(s2); System.out.println(stu.size()); } }View Code
4.Properties屬性類物件的相關方法:
Properties是一個Map集合,繼承Hashtable,Properites的key和value都是String型別
Properties被稱為屬性類物件。是執行緒安全的。
public class Test { public static void main(String[] args) { //建立Properties物件 Properties pro = new Properties(); //存 pro.setProperty("123", "a"); pro.setProperty("456", "b"); pro.setProperty("789", "c"); //通過key獲取value String s1 = pro.getProperty("123"); String s2 = pro.getProperty("456"); String s3 = pro.getProperty("789"); System.out.println(s1); System.out.println(s2); System.out.println(s3); } }View Code
上一篇:十一、集合:Collection
下一篇:持續更新中