一載體java
HashMap是由數組組成,數組元素爲哈希鏈。node
數組數組
public class MyHashMap<K, V> { transient Node<K, V>[] table; }
數組元素ide
@SuppressWarnings("hiding") class Node<K, V> implements Map.Entry<K, V> { final int hash; final K key; V value; Node<K, V> next; Node(int hash, K key, V value, Node<K, V> next) { this.hash = hash; this.key = key; this.value = value; this.next = next; } @Override public K getKey() { return key; } @Override public V getValue() { return value; } @Override public V setValue(V value) { V tempValue = value; this.value = value; return tempValue; } @Override public String toString() { return "Node [ key=" + key + " , value=" + value + " , " + "]"; } @Override public int hashCode() { return Objects.hashCode(key) ^ Objects.hashCode(value); } public final boolean equals(Object o) { if (o == this) return true; if (o instanceof Map.Entry) { Map.Entry<?, ?> e = (Map.Entry<?, ?>) o; if (Objects.equals(key, e.getKey()) && Objects.equals(value, e.getValue())) return true; } return false; } }
二屬性this
private static int initCount = 16;//初始化長度 private static float loadFator = 0.75f;//加載因子 private int size; private static int threshold;//擴容臨界值 static final int MAXIMUM_CAPACITY = 1 << 30;
添加構造方法初始化屬性spa
public MyHashMap(){ this(initCount, loadFator); } public MyHashMap(int initCount){ this(initCount, loadFator); } public MyHashMap(int initCount, float loadFator){ if(initCount < 0){ throw new IllegalArgumentException("初始化長度不合法 : " + initCount); } if(initCount > MAXIMUM_CAPACITY){ initCount = MAXIMUM_CAPACITY; } if(loadFator <0 || Float.isNaN(loadFator)){ throw new IllegalArgumentException("加載因子不合法 : " + loadFator); } this.loadFator = loadFator; this.threshold = (int) (initCount * 2 * loadFator); }
三方法指針
1增長元素code
1.1若是沒有哈希碰撞,HashMap就至關於一個數組,並且查找無需遍歷。blog
public Object put(K key, V value){ if(table == null || table.length == 0){ //此時真正建立數組 table = new Node[initCount]; } int hash = hash(key); int len = table.length; int index = hash % len;// (len-1) & hash if(table[index] == null){ table[index] = new Node<>(hash, key, value, null); } return null; } static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }
1.2哈希碰撞的狀況。索引
若是按jdk8的實現方式,哈希碰撞的狀況分爲兩種狀況,首先是造成一個鏈表,當鏈表長度大於8時,會分裂成紅黑樹。這裏只按鏈表實現。
else{//哈希碰撞 MyHashMap<K, V>.Node<K, V> e = p; do{ if(e.hash == hash && ((e.key == key) || (key != null && key.equals(e.key)))){ V oldValue = p.value; //用新值覆蓋舊值 p.value = value; return oldValue; } p = e; }while((e = p.next) != null); //將元素放在鏈表最前面 table[index] = new Node<K, V>(hash, key, value, p); if(++size > threshold){//擴容 resize(); }
public void resize(){ //TODO }
2獲取元素
根據key獲得數組索引,再判斷有無鏈表。若有,判斷鏈表長度。爲1直接返回。大於1,需循環鏈表。
public V get(K k){ int hash = hash(k); int len = table.length; int index = hash % len; Node<K,V> node = table[index];//找到鏈表 if (node == null) { return null; } Node<K,V> p; if ((p = node.next) == null) {//若是鏈表只有一個元素,直接返回 return node.value; } else {//若是有多個元素,循環比較key p = node; do { if (p.key == k || p.key.equals(k)) { return p.value; } node = p; } while ((p = node.next) != null); } return null; }
3刪除元素
首先得找到元素。一樣是根據key獲得數組索引。判斷鏈表是否有值。無值直接返回。有值分兩種狀況,一種是被刪除元素在鏈表最前面,那麼直接將最前面的指針斷掉。不然須要將前一個的指針指向後一個元素。因爲鏈表結構只有next,沒有先後左右,因此在循環的時候須要隨時保存前一個元素,在找到被刪除元素的時候,直接將前一個與後一個鏈接便可。
public void remove(K k){ int hash = hash(k); int len = table.length; int index = hash % len; Node<K,V> node = table[index]; Node<K,V> prev = node; while (node != null) { if(node.hash == hash && (node.key == k || node.key.equals(k))){ if(node == prev){//被刪除元素在鏈表第一位 table[index] = node.next; }else{ prev.next = node.next;//node 爲當前元素 prev爲前一個元素 將前一個元素的指針next指向下一個元素 } size--; } prev = node; node = node.next; }; }
四擴容
擴容的觸發條件是屬性threshold大於HashMap元素個數size。在put元素的時候須要判斷。
在增長,刪除的時候,會對元素個數進行增減。
注意這裏的size並非指數組長度。而是指鏈表的總長度。
初始化一個HashMap,在put入第一個值的時候,會初始化一個數組,長度爲16,算上負載因子,實際使用長度爲12。但並非說這個數組必需要每一個索引都有值纔會擴容。以下圖所示,只有3個索引有值,但3個索引處的鏈表總長度達到12,也就達到了擴容的臨界點。
HashMap擴容首先須要將數組擴容。數組長度改變,那麼全部元素的鏈表必須從新組合。否則,查找就會亂套。這是比較耗時的。因此,在使用HashMap的時候,若是能預估長度,最好在初始化的時候指定,避免頻繁擴容。
public void resize(){ int len = size << 1; threshold = (int)(len * loadFator); size = 0; Node<K,V>[] newTable = new Node[len]; Node<K,V>[] tempTable = table; table = newTable; int tempSize = tempTable.length; for(int i=0; i<tempSize; i++){ Node<K,V> node = tempTable[i]; while(node != null){ put(node.key, node.value); node = node.next; } } }
五迭代
1經過keyset來迭代
keyset獲得全部的鍵的set集合。再經過set的迭代器來迭代。
首先定義一個set集合。
Set<K> keySet;
定義獲取keyset的方法
public Set<K> keySet(){ return keySet == null ? (keySet = new KeySet()) : null; }
每次keySet爲空,須要經過一個內部類KeySet獲取。
class KeySet extends AbstractSet<K>{ @Override public Iterator<K> iterator() { return new newKeyIterator(); } @Override public int size() { return size; } }
繼續定義newKeyIterator
final class KeyIterator extends HashIterator implements Iterator<K> { public final K next() { return nextNode().key; } }
繼續定義KeyIterator
public class HashIterator{ Node<K,V>[] nodes; Node<K,V> prve; Node<K,V> next; int index; public HashIterator(){ nodes = table; index = 0; prve = next = null; do { prve = next = table[index]; } while((++index < table.length) && next == null); } final Node<K,V> nextNode(){ Node<K,V> e = next; if((next = (prve = e).next) == null && nodes != null){ do { } while(index < table.length && (next = nodes[index++]) == null); } return e; } public boolean hasNext() { return next != null; } }
再執行一個迭代的時候,先獲得一個keys的集合,而後根據集合獲得迭代器,這時候會執行HashIterator的構造方法,目的是找到第一個鏈表。
Set<String> keys = map.keySet();
Iterator<String> it = keys.iterator();
而後執行,next時會執行HashIterator的hashNext方法和nextNode方法。
while(it.hasNext()){
String key = it.next();
String value = map.get(key);
}
2經過entrySet。
Set<Map.Entry<K,V>> entrySet; public Set<Map.Entry<K,V>> entrySet(){ return entrySet == null ? (entrySet = new EntrySet()) : null; } class EntrySet extends AbstractSet<Map.Entry<K,V>>{ @Override public Iterator<Map.Entry<K,V>> iterator() { return new EntryIterator(); } @Override public int size() { return size; } } final class EntryIterator extends HashIterator implements Iterator<Map.Entry<K,V>> { public final Node<K,V> next() { return nextNode(); } }
最終代碼
import java.util.AbstractSet; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.Iterator; public class MyHashMap<K, V> { transient Node<K, V>[] table; private static int initCount = 16;//初始化長度 private static float loadFator = 0.75f;//加載因子 private int size; private static int threshold;//擴容臨界值 static final int MAXIMUM_CAPACITY = 1 << 30; public MyHashMap(){ this(initCount, loadFator); } public MyHashMap(int initCount){ this(initCount, loadFator); } public MyHashMap(int initCount, float loadFator){ if(initCount < 0){ throw new IllegalArgumentException("初始化長度不合法 : " + initCount); } if(initCount > MAXIMUM_CAPACITY){ initCount = MAXIMUM_CAPACITY; } if(loadFator <0 || Float.isNaN(loadFator)){ throw new IllegalArgumentException("加載因子不合法 : " + loadFator); } this.loadFator = loadFator; this.threshold = (int) (initCount * loadFator); } public Object put(K key, V value){ if(table == null || table.length == 0){ //此時真正建立數組 table = new Node[initCount]; } int hash = hash(key); int len = table.length; int index = (len-1) & hash; Node<K,V> p; if((p = table[index]) == null){ table[index] = new Node<K, V>(hash, key, value, null); }else{//哈希碰撞 MyHashMap<K, V>.Node<K, V> e = p; Node<K,V> temp = p; do{ if(e.hash == hash && ((e.key == key) || (key != null && key.equals(e.key)))){ V oldValue = p.value; //用新值覆蓋舊值 p.value = value; return oldValue; } temp = e; }while((e = temp.next) != null); //將元素放在鏈表最前面 table[index] = new Node<K, V>(hash, key, value, p); } if(++size > threshold){//擴容 resize(); } return null; } public V get(K k){ int hash = hash(k); int len = table.length; int index = (len-1) & hash; Node<K,V> node = table[index];//找到鏈表 if (node == null) { return null; } Node<K,V> p; if ((p = node.next) == null) {//若是鏈表只有一個元素,直接返回 return node.value; } else {//若是有多個元素,循環比較key p = node; do { if (p.key == k || p.key.equals(k)) { return p.value; } node = p; } while ((p = node.next) != null); } return null; } public void remove(K k){ int hash = hash(k); int len = table.length; int index = (len-1) & hash; Node<K,V> node = table[index]; Node<K,V> prev = node; while (node != null) { if(node.hash == hash && (node.key == k || node.key.equals(k))){ if(node == prev){//被刪除元素在鏈表第一位 table[index] = node.next; }else{ prev.next = node.next;//node 爲當前元素 prev爲前一個元素 將前一個元素的指針next指向下一個元素 } size--; } prev = node; node = node.next; }; } Set<K> keySet; public Set<K> keySet(){ return keySet == null ? (keySet = new KeySet()) : null; } Set<Map.Entry<K,V>> entrySet; public Set<Map.Entry<K,V>> entrySet(){ return entrySet == null ? (entrySet = new EntrySet()) : null; } class EntrySet extends AbstractSet<Map.Entry<K,V>>{ @Override public Iterator<Map.Entry<K,V>> iterator() { return new EntryIterator(); } @Override public int size() { return size; } } final class EntryIterator extends HashIterator implements Iterator<Map.Entry<K,V>> { public final Node<K,V> next() { return nextNode(); } } class KeySet extends AbstractSet<K>{ @Override public Iterator<K> iterator() { return new KeyIterator(); } @Override public int size() { return size; } } final class KeyIterator extends HashIterator implements Iterator<K> { public final K next() { return nextNode().key; } } public class HashIterator{ Node<K,V>[] nodes; Node<K,V> prve; Node<K,V> next; int index; public HashIterator(){//獲得 keys.iterator();的時候執行此構造方法,找到第一個鏈表 //找到第一個元素 nodes = table; index = 0; prve = next = null; do { prve = next = table[index]; } while((++index < table.length) && next == null); } final Node<K,V> nextNode(){ Node<K,V> e = next; if((next = (prve = e).next) == null && nodes != null){//循環鏈表 do { } while(index < table.length && (next = nodes[index++]) == null);//找到下一個鏈表 } return e; } public boolean hasNext() { return next != null; } } public void resize(){ int len = size << 1; threshold = (int)(len * loadFator); size = 0; Node<K,V>[] newTable = new Node[len]; Node<K,V>[] tempTable = table; table = newTable; int tempSize = tempTable.length; for(int i=0; i<tempSize; i++){ Node<K,V> node = tempTable[i]; while(node != null){ put(node.key, node.value); node = node.next; } } } static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); } @SuppressWarnings("hiding") class Node<K, V> implements Map.Entry<K, V> { final int hash; final K key; V value; Node<K, V> next; Node(int hash, K key, V value, Node<K, V> next) { this.hash = hash; this.key = key; this.value = value; this.next = next; } @Override public K getKey() { return key; } @Override public V getValue() { return value; } @Override public V setValue(V value) { V tempValue = value; this.value = value; return tempValue; } @Override public String toString() { return "[" + key + " : " + value + "]" + " next " + next; } @Override public int hashCode() { return Objects.hashCode(key) ^ Objects.hashCode(value); } public final boolean equals(Object o) { if (o == this) return true; if (o instanceof Map.Entry) { Map.Entry<?, ?> e = (Map.Entry<?, ?>) o; if (Objects.equals(key, e.getKey()) && Objects.equals(value, e.getValue())) return true; } return false; } } }