一、HashMap的底層實現
HashMap底層是基於陣列和連結串列實現的。其中最重要的引數:容量和負載因子。
容量的預設大小事16,負載因子是0.75,當HashMap的size>16*0.75的時候就會發生庫容(容量和負載因子都可以自由調整)
Hashmap實現了Map介面,允許放入null元素,出了該類未實現同步外,其餘和HashTable大致相同,跟TreeMap不同,該容器不保證冤死順序,根據需要該容器可能對元素重新雜湊,元素的順序也會被重新打散,因此不同時間迭代同一個HashMap的順序可能會不同。
二、HashMap的put(key,value)方法
首先會將傳入的可以、做hash運算計算出hashCode,然後根據陣列長度取模計算出在陣列中的index下表
由於在計算機中位運算比取模運算效率高,所以HashMsap規定陣列的長度為2n。這樣用2n-1做位運算與取模效果一致,並且效率要高出許多
由於陣列的長度有限,所以難免出現不同放入key通過運算得到的index相同,這種情況可以利用連結串列來解決,HashMap會在table[index]出形成連結串列,採用頭插法將資料插入連結串列中
三、HashMap的get(key)fangfa
get和put類似,也是講傳入的可以計算出index,如果該位置上是一個連結串列就需要比那裡整個連結串列,通過key.equals(k)來找到對應的元素。
遍歷方式:
第一種
Iterator<Map.Entry<String, Integer>> entryIterator=map.entrySet().iterator();
while(entryIterator.hasNext()){
Map.Entry<String,Integer> next=entryIterator.next();
System.err.println("key="+next.getKey()+"value="+next.getValue());
}
第二種
Iterator iterator=map.keySet().iterator();
while(iterator.hasNext()){
String key=iterator.next();
System.err.println("key="+key+"value="+map.get(key));
}
第三種
map.forEach((key,value)->{
System.err.println("key="+key+"value="+value);
});
第一種可以把key value同時取出,第二種還得需要通過key去一次value,效率較低,第三種需要JDK1.8以上,通過外層遍歷table,內層遍歷連結串列或紅黑樹。
四、為什麼多執行緒場景下不推薦使用HashMap
在併發環境下使用HashMap容易出現死迴圈。併發場景下發生擴容,呼叫resize()方法裡的rehash()時,容易出現環形連結串列。這樣當獲取一個不存在的key時,計算出的index正好是環形連結串列的下標時就會出現死迴圈
所以,HashMap只能在單執行緒中使用,並且儘量的預設容量,儘可能的減少擴容發
在JDK1.8中對HashMap進行了優化:當hash碰撞之後寫入連結串列的長度超過閾值(預設為8),連結串列將會轉換成紅黑樹。假設hash衝突非常嚴重,一個陣列後面接了很長的連結串列,此時查詢的時間複雜度就是O(n)。如果是紅黑樹,時間複雜度就是O(logn)。大大提高了查詢的效率。多執行緒場景下推薦使用ConcurrentHashMap。
五、HashSet的底層實現
HashSet是對HashMap的簡單包裝,對HashSet的函式呼叫都會轉換成合適的HashMap方法,因此HashSet的實現非常簡單。
成員變數
首先了解下HashSet的成員變數
private transient HashMap<E,Object> map;
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
發現主要有兩個變數:
map:用於存放最終資料
PRESENT:是所有寫入map的value值
建構函式
public HashSet() {
map = new HashMap<>();
}
public HashSet(int initialCapacity, float loadFactor) {
map = new HashMap<>(initialCapacity, loadFactor);
}
public HashSet(int initialCapacity) {
map = new HashMap<>(initialCapacity);
}
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}
public HashSet(Collection<? extends E> c) {
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
建構函式很簡單,利用了HashMap初始化了map
add
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
比較關鍵的就是這個add()方法。可以看出他是將存放的物件當做了HashMap的鍵,value都是相同的PRESENT.由於HashMap的key是不能重複的,所以每當有重複的值寫入到HashSet中只能存放不重複的元素