HashMap做爲一個常常用到的類,先今後類開始閱讀。java
####存儲原理圖 node
####1、 成員變量數組
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
默認的桶數量app
static final int MAXIMUM_CAPACITY = 1 << 30;
桶的最大數量ui
static final float DEFAULT_LOAD_FACTOR = 0.75f;
默認負載係數this
static final int TREEIFY_THRESHOLD = 8;
桶內元素樹化的最少個數spa
static final int UNTREEIFY_THRESHOLD = 6;
反樹化時, 桶內元素的最多個數code
final float loadFactor;
設置的負載係數對象
static final int MIN_TREEIFY_CAPACITY = 64;
桶內元素就行樹化的時候,整個容器的須要達到的最小容量繼承
transient int modCount;
容器內部發生結構性改變的次數(用於iterator遍歷)
transient int size;
容器內部存儲的元素個數
transient Node<K,V>[] table;
存儲桶的數組,這個是HashMap的核心
int threshold;
進行再次內存分配的時候。元素須要達到的個數
transient Set<Map.Entry<K,V>> entrySet;
提供map的訪問接口
####2、公有方法
public void clear()
清空內部元素。
public V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)
獲取與key對應的value,而且將它們做爲參數調用remappingFunction(),獲得新的value。
若是key原來不存在的話,將新的key,value添加進去,可是value不能爲空值。
public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)
根據key去查找node. 分爲如下幾種狀況 :
public V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)
根據key去查找node,若node存在,則使用mappingFunction(key)去更新值,不存在馬上返回。
public boolean containsKey(Object key)
根據key去查找node,調用內部的getNode方法
public V put(K key, V value)
將鍵值對放入bucket內部,此處注意可能要進行treeify。
public boolean containsValue(Object value)
遍歷table,而且遍歷每一個bucket
public Set<Map.Entry<K,V>> entrySet()
返回一個EntrySet類型對象,EntrySet類型繼承自AbstractSet,咱們主要用這個類來進行對HashMap的遍歷
public void forEach(BiConsumer<? super K, ? super V> action)
針對每個內部存儲的元素,調用action.accept()方法,在調用過程當中,modCount不能改變,這就是爲何HashMap的迭代是failFast的,若是改變就會拋出異常。
public V get(Object key)
獲取key對應的value
public V getOrDefault(Object key, V defaultValue)
JDK8新方法,若是value爲null,返回defaultValue。
public boolean isEmpty()
返回size == 0
public Set<K> keySet()
返回一個包含全部key的Set,能夠對這個set進行迭代,而後遍歷
public V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction)
經過key去查詢一個已存在的鍵值對,而且將oldValue與newValue傳遞給remappingFunction進行計算後從新賦值給對應key
public V put(K key, V value)
將鍵值對存入map內,涉及到resize以及treeify
public void putAll(Map<? extends K, ? extends V> m)
經過entrySet()遍歷m,將m內的全部元素插入新的map
public V putIfAbsent(K key, V value)
若是key對象的鍵值對不存在,則存入新的鍵值對
public V remove(Object key)
經過key去檢索鍵值對,而且刪除
public boolean remove(Object key, Object value)
經過key去檢索鍵值對,而且經過equals方法比較檢索出來的value,若value相等則移除
public boolean replace(K key, V oldValue, V newValue)
若是key對應的map內的value爲oldValue,則將其替換爲newValue
public V replace(K key, V value)
強key對象的值替換爲value
public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function)
對於全部的鍵值對調用function,apply()方法,並將返回值做爲新的value
public int size()
返回大小
public Collection<V> values()
獲取全部的值的Collection(可是並非說把全部的值存儲在了相似於List這樣的集合裏面了,咱們只是能夠調用Collection的接口而已)
####3、簡單實現
package com.braon.jdk; public class SimpleHashMap<K, V> { private Node<K, V>[] table; private int capacity = 4; private float loadFactor = 0.75f; private int threshold = 3; private int size; public SimpleHashMap() { init(); } public SimpleHashMap(int capacity, int loadFactor) { this.capacity = capacity; this.loadFactor = loadFactor; this.threshold = capacity * loadFactor; init(); } @SuppressWarnings("unchecked") private void init() { capacity = 1 << 4; table = new Node[capacity]; size = 0; } public void clear() { init(); } public void put(K K, V V) { if (K == null || V == null) { return; } // space is narrow else if (size + 1 > threshold) { resize(); } Node<K, V> newNode = new Node<>(); newNode.setK(K); newNode.setV(V); newNode.setHash(K.hashCode() & (capacity - 1)); newNode.setNext(null); if (putNode(newNode)) size++; } public V get(K K) { // calculate store place int hash = K.hashCode() & (capacity - 1); Node<K, V> first = table[hash]; if (first != null) { do { if (first.getK().equals(K)) return first.getV(); } while ((first = first.next) != null); return null; } // if else return null; } @SuppressWarnings("unchecked") private void resize() { // recaculate the array size capacity = capacity << 1; threshold = (int) (loadFactor * capacity); // keep the old one, and renew a new table Node<K, V>[] oldTable = table; table = new Node[capacity]; // relay the elements for (Node<K, V> first : oldTable) { while (first != null) { first.setHash(first.getK().hashCode() & (capacity - 1)); this.putNode(first); // we need to remove the link Node<K, V> next = first.next; first.next = null; first = next; } // while } // for } public int getSize() { return size; } public int getCapacity() { return capacity; } private boolean putNode(Node<K, V> newNode) { Node<K, V> first = table[newNode.getHash()]; if (first == null) { table[newNode.getHash()] = newNode; } else { while (first.next != null) { // two absolute equivalent K, we need to update the V if (first.getK().equals(newNode.getK())) { first.setV(newNode.getV()); return false; } first = first.next; } if (first.getK().equals(newNode.getK())) { first.setV(newNode.getV()); return false; } else first.next = newNode; } return true; } public void print() { System.out.println(toString() + "\r\n"); } public void remove(K k) { if (k == null) return; int hash = k.hashCode() & (capacity - 1); Node<K, V> prev = table[hash]; Node<K, V> cur = table[hash].next; if (prev.getK().equals(k)) { table[hash] = table[hash].next; size--; return; } while (cur != null) { if (cur.getK().equals(k)) { prev.next = cur.next; size--; return; } prev = cur; cur = cur.next; } } public String toString() { StringBuffer sb = new StringBuffer(); for (Node<K, V> node : table) { if (node != null) { do { sb = sb.append("hashCode: " + node.getHash() + ", K: " + node.getK() + ", V: " + node.getV() + " "); } while ((node = node.next) != null); sb.append("\r\n"); } } return sb.toString(); } } class Node<K, V> { Node<K, V> next; private K k; private V v; private int hash; public Node() { } public Node(K k, V v) { this.k = k; this.v = v; } public int getHash() { return hash; } public void setHash(int hash) { this.hash = hash; } public K getK() { return k; } public void setK(K k) { this.k = k; } public Node<K, V> getNext() { return next; } public void setNext(Node<K, V> next) { this.next = next; } public V getV() { return v; } public void setV(V v) { this.v = v; } }
####注意點 一、須要同時重載equals和hashCode方法 二、size增加後又減少,可是capacity不會減少 三、使用entrySet()進行遍歷,比較快,使用keySet()遍歷,相對較慢 四、hashCode方法的編寫須要注意,最好不要有多個值產生相同的hashCode,否則對查詢產生影響 五、loadfactor最好不要改變 六、查詢次數較多,插入次數相對較少,且元素hashCode相對集中,請使用TreeHashMap 七、values()方法獲取的返回值並無存儲了任何一個value的引用,只是咱們能夠用Collection接口的方法去調用這個返回值而已 八、耗時較多的插入通常都是由於須要resize或者treeify