實現了Map、Cloneable、Serializable接口,繼承了AbstractMap類面試
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable /** * Map接口: 實現鍵值對,Map接口規定了一個key對應一個value * HashMap使用該接口用來替換Dictionary類 * * AbstractMap類: 繼承Map的抽象類,減小Map操做的實現 * * Cloneable接口: 能夠顯示的調用Object.clone()方法,合法的對該類 * 實例進行字段複製 * * Serializable接口: 實現該接口後,該類能夠被序列化和反序列化 */
HashMap是線程不安全的,在併發的環境下可使用ConcurrentHashMap。數組
內部實現:在JDK1.8以前是數組+鏈表,JDK1.8以後是數組+鏈表+紅黑樹
加入紅黑樹的緣由:JDK1.8以前HashMap使用的是數組加鏈表,因爲哈希函數不能百分百的讓元素均勻的分佈,就會形成有大量的元素存入同一個index(桶)下,這樣index就造成了一條很長的鏈表,由此元素的遍歷的時間複雜度爲O(n),失去了HashMap的優點,加入了紅黑樹,查找的時間複雜度爲O(log n),實現了優化
數組的特色:查找快,時間複雜度是O(1),增刪慢,時間複雜度是O(n)
鏈表的特色:查找慢,時間複雜度是O(n),增刪快,時間複雜度是O(1)
紅黑樹的特色:在遍歷鏈表的基礎上,紅黑樹查找的時間複雜度是O(log n)
紅黑樹的查詢效率遠大於鏈表,可是插入/刪除要比鏈表慢安全
1.默認初始容量:16,必須是2的整數次方併發
2.默認加載因子:0.75函數
3.閾值:容量*加載因子性能
4.樹形閾值:默認是8,當bucket中的鏈表長度大於8時,則進行鏈表樹化優化
5.非樹形閾值:默認是6,當進行擴容時,當進行擴容(resize())時(這時候的HashMap的數據存儲位置會從新計算),在計算完後,原有的紅黑樹內的數量<6時,則由紅黑樹轉換爲鏈表spa
6.樹形最小容量:桶多是樹的哈希表的最小容量,至少是TREEIFY_THRESHOLD 的 4 倍,這樣能避免擴容時的衝突線程
//鏈表轉紅黑樹的閾值 static final int TREEIFY_THRESHOLD = 8; //紅黑樹轉鏈表的閾值 static final int UNTREEIFY_THRESHOLD = 6; /** *最小樹形化容量閾值:即 當哈希表中的容量 > 該值時,才容許樹形化鏈表 (即 將鏈表 轉換成紅黑樹) *不然,若桶內元素太多時,則直接擴容,而不是樹形化 *爲了不進行擴容、樹形化選擇的衝突,這個值不能小於 4 * TREEIFY_THRESHOLD **/ static final int MIN_TREEIFY_CAPACITY = 64;
只有在數組的長度大於64時,且鏈表的長度>8時,才能樹形化鏈表
鏈表的長度大於8時會調用treeifyBin方法轉換爲紅黑樹,可是treeifyBin方法內部有一個判斷,當只有數組的長度>64的時候,才能將鏈表樹形化,不然只進行resize擴容
由於鏈表過長而數組太短,會常常發生hash碰撞,這時進行樹形化的做用不大,由於鏈表過長的緣由就是數組太短。樹形化以前要檢查數組的長度,<64進行擴容,而不是進行樹形化
鏈表的長度>8,但數組的長度<64時,不會進行樹形化,而是進行resize後rehash從新排序設計
添加:V put(K key,V value) -->添加元素(也能夠實現修改)
刪除:void clear() -->清空全部鍵值對元素
V remove(Object key) -->根據鍵刪除對應的值,並把值返回
判斷:containsKey(Object key) -->是否包含指定的鍵
containsValue(Object value)–>是否包含指定的值
遍歷:Set<Map.Entry<K,V>> entrySet()–>獲取鍵值對
V get(Object key) -->根據鍵獲取值
Collection value()–>獲取值的集合
獲取:Set setKey()–>獲取鍵的集合
int size()–>獲取集合元素的個數
基本方法的使用
HashMap<Integer,String> map=new HashMap<>(); //添加元素 map.put(1, "a"); map.put(2, "b"); map.put(3, "c"); //鍵不可重複,值被覆蓋 map.put(3, "C"); //經過鍵刪除整個鍵值對 map.remove(3); //清空 map.clear(); //是否爲空 System.out.println(map.isEmpty());//false //是否包含4 System.out.println(map.containsKey(4));//false //是否包含「b」值 System.out.println(map.containsValue("b"));//true //獲取集合元素個數 System.out.println(map.size());//3 //經過鍵獲取值 System.out.println(map.get(3));//C //獲取全部值構成的集合 Collection<String> values=map.values(); for(String v:values){ System.out.println("值:"+v);//值:a 值:b 值:c } System.out.println(map); }
public static void main(String[] args) { Map<String,Integer> map=new HashMap<>(); map.put("小林",21); map.put("小張",35); map.put("小王",18); demo1(map); demo2(map); } //經過Set<K> setKey()方法遍歷 private static void demo1(Map<String,Integer> map) { Set<String> keys=map.keySet(); for (String key:keys){ Integer value=map.get(key); System.out.println("鍵爲:"+key+"值爲:"+value);//鍵爲:小林值爲:21 //鍵爲:小王值爲:18 //鍵爲:小張值爲:35 } } //經過Set<Map.Entry<K,V>> entrySet()方法遍歷 private static void demo2(Map<String, Integer> map) { Set<Map.Entry<String,Integer>> kvs=map.entrySet(); for (Map.Entry<String,Integer> kv:kvs){ String kstring=kv.getKey(); Integer istring=kv.getValue(); System.out.println(kstring+"-----"+istring); //小林-----21 //小王-----18 //小張-----35 } }
1.緣由:
當對某個元素進行哈希運算後,獲得一個存儲地址,在進行插入的時候,發現已經被其餘元素佔用了。這就是所謂的哈希衝突,也叫哈希碰撞。
哈希函數的設計相當重要,好的哈希函數會盡可能的確保計算簡單和散列地址分佈均勻,可是,數組是一塊連續的固定的長度的內存空間,再好的哈希函數也不能保證獲得的存儲地址絕對不發生衝突。
2.解決hash衝突的方法:
開放地址法:發生衝突,繼續尋找下一塊未被佔用的存儲地址。
再散列函數法:當發生衝突,使用第二個、第三個、哈希函數計算地址,直到無衝突。
鏈地址法:將全部關鍵字的同義詞的記錄存儲在一個單鏈表中,咱們稱這種表爲同義詞子表。
HashMap採用的是鏈地址法,也就是數組+鏈表的方式
HashMap由數組+鏈表組成的,數組是HashMap的主體,鏈表則是主要爲了解決哈希衝突而存在的,若是定位到的數組位置不含鏈表(當前的entry的next指向null),那麼對於查找,添加等操做很快,僅需一次尋址便可;若是定位到的數組包含鏈表,對於添加操做,其時間複雜度爲O(n),首先遍歷鏈表,存在即覆蓋,不然新增,而後經過key對象的equals方法逐一比對查找。因此!對於性能考慮,HashMap中的鏈表出現越少,性能纔會越好。
緣由:因爲數組擴容後,同一索引位置的節點順序會反掉
7.HashMap和Hashtable的區別
重寫equals()方法,須要重寫hashCode()方法。
hashCode規定:兩個對象相等(即equals()返回true),hashCode必定相同;兩個對象hashCode相同,兩個對象不必定相等;
重寫equals,而不重寫hashCode方法,默認調用的是Object類的hashCode()方法,會致使兩個對象的equals相同但hashCode不一樣。簡單的來講,重寫equals方法後,重寫hashCode方法就是爲了確保比較的兩個對象是同一對象。
LinkedHashMap的底層結構和HashMap是相同的,由於LinkedHashMap繼承了HashMap,
區別在於:LinkedHashMap內部提供了Entry,替換了HashMap中的Node。
LinkedHashMap:保證在遍歷map元素時,能夠照添加的順序實現遍歷
緣由:在原來的HashMap底層結構的基礎上,增長了一對指針,指向前一個和後一個元素
對於頻繁的遍歷操做,LinkedHashMap的效率要高於HashMap
保證照key-value對進行排序,實現排序遍歷,此時考慮key的天然排序或者定製排序,底層使用的是紅黑樹
元素根據鍵排序,元素具備惟一性
1)天然排序
讓元素所在的類繼承天然排序Comparable
2)比較器排序
讓集合的構造方法接收一個比較器接口(Comparator的實現對象)
做爲古老的實現類;線程安全的,效率低;不能存儲null的key和value
你們看完有什麼不懂的能夠在下方留言討論,也能夠關注我私信問我,我看到後都會回答的。也歡迎你們關注個人公衆號:前程有光,金三銀四跳槽面試季,整理了1000多道將近500多頁pdf文檔的Java面試題資料,文章都會在裏面更新,整理的資料也會放在裏面。謝謝你的觀看,以爲文章對你有幫助的話記得關注我點個贊支持一下!