hashmap的基本原理
hashMap基本注意点
-
key不可重复,值可重复
-
底层是哈希表
-
线程不安全
-
允许key为null,value也可为null
-
jdk1.8 链表长度大于8的时候,会将链表转换为红黑树
hashMap的数据结构

HashMap Node节点
1 //node是个静态的内部类,存着key-vale的值 2 static class Node<K,V> implements Map.Entry<K,V> { 3 final int hash;//保存该桶的hash值 4 final K key;//不可改变的hash值 5 V value; 6 Node<K,V> next;//指向桶hash的下一节点 7 8 Node(int hash, K key, V value, Node<K,V> next) { 9 this.hash = hash; 10 this.key = key; 11 this.value = value; 12 this.next = next; 13 } 14 15 public final K getKey() { return key; } 16 public final V getValue() { return value; } 17 public final String toString() { return key + "=" + value; } 18 19 public final int hashCode() { 20 return Objects.hashCode(key) ^ Objects.hashCode(value); 21 } 22 23 public final V setValue(V newValue) { 24 V oldValue = value; 25 value = newValue; 26 return oldValue; 27 } 28 29 public final boolean equals(Object o) { 30 if (o == this) 31 return true; 32 if (o instanceof Map.Entry) { 33 Map.Entry<?,?> e = (Map.Entry<?,?>)o; 34 if (Objects.equals(key, e.getKey()) && 35 Objects.equals(value, e.getValue())) 36 return true; 37 } 38 return false; 39 } 40 }
如何通过key大hashcode来确定桶的位置
1 //这个确定了hash的值,这是第一步 2 static final int hash(Object key) { 3 int h; 4 return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); 5 } 6 //后面的putVal会通过数组的长度与hash的值来最终确定桶的位置 7 //但是存入hash值还是hash值 8 //i为准确数组的位置 9 if ((p = tab[i = (n - 1) & hash]) == null)
hashMap的基本变量
1 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; //默认桶的数量 2 static final int MAXIMUM_CAPACITY = 1 << 30;//最大值 3 static final float DEFAULT_LOAD_FACTOR = 0.75f;//负载因子 4 int threshold;//能够容纳key_value的数量 5 int size;//key的个数
threshold=负载因子*length,负载因子越大,所能容纳的元素个数越多
注意
如果空间比较大,要求速度比较快,那么既可以将负载因子调小,以降低冲突
hashMap的put方法实现
1 final V putVal(int hash, K key, V value, boolean onlyIfAbsent, 2 boolean evict) { 3 Node<K,V>[] tab; Node<K,V> p; int n, i; 4 //判断table是否为空,我空就创建一个并获得数组的长度 5 if ((tab = table) == null || (n = tab.length) == 0) 6 n = (tab = resize()).length; 7 //判断计算出来的桶位置是否为空,为空的化,直接new一个值放在这个位置 8 if ((p = tab[i = (n - 1) & hash]) == null) 9 tab[i] = newNode(hash, key, value, null); 10 else { 11 //如果这个桶有的化 12 Node<K,V> e; K k; 13 //判断如果key值相同,就直接替代 14 if (p.hash == hash && 15 ((k = p.key) == key || (key != null && key.equals(k)))) 16 e = p; 17 else if (p instanceof TreeNode)//如果为红黑树,就直接放入 18 e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); 19 else { 20 for (int binCount = 0; ; ++binCount) {//放值 21 if ((e = p.next) == null) { 22 p.next = newNode(hash, key, value, null); 23 if (binCount >= TREEIFY_THRESHOLD - 1) // 如果链表长度大于8的化,就直接变为树 24 treeifyBin(tab, hash); 25 break; 26 }//判断索引每个元素是否key相同,直接替代 27 if (e.hash == hash && 28 ((k = e.key) == key || (key != null && key.equals(k)))) 29 break; 30 p = e; 31 } 32 } 33 //替换原来的值,并返回原来的值 34 if (e != null) { // existing mapping for key 35 V oldValue = e.value; 36 if (!onlyIfAbsent || oldValue == null) 37 e.value = value; 38 afterNodeAccess(e); 39 return oldValue; 40 } 41 } 42 ++modCount;//没有相同的key,结构修改次数 43 if (++size > threshold) 44 resize(); 45 afterNodeInsertion(evict); 46 return null; 47 }
-
判断table是否为空
-
计算的到的位置,是否有值
-
判断是否同一个key
-
加入一个值后,判断该链表是否大于8,转换红黑数结构
-
-
替换key的value会返回老的value
Hashmap的get方法
1 final Node<K,V> getNode(int hash, Object key) { 2 Node<K,V>[] tab; Node<K,V> first, e; int n; K k; 3 //判断不能为空 4 if ((tab = table) != null && (n = tab.length) > 0 && 5 (first = tab[(n - 1) & hash]) != null) { 6 if (first.hash == hash && // always check first node 7 ((k = first.key) == key || (key != null && key.equals(k)))) 8 return first;//第一是就直接返回 9 if ((e = first.next) != null) { 10 if (first instanceof TreeNode)//树状 11 return ((TreeNode<K,V>)first).getTreeNode(hash, key); 12 do {//链表遍历查找 13 if (e.hash == hash && 14 ((k = e.key) == key || (key != null && key.equals(k)))) 15 return e; 16 } while ((e = e.next) != null); 17 } 18 } 19 return null; 20 }
hashMap的扩容机制
resize方法
1 final Node<K,V>[] resize() { 2 Node<K,V>[] oldTab = table; 3 int oldCap = (oldTab == null) ? 0 : oldTab.length; 4 int oldThr = threshold; 5 int newCap, newThr = 0; 6 if (oldCap > 0) { 7 if (oldCap >= MAXIMUM_CAPACITY) { 8 threshold = Integer.MAX_VALUE;//阀值直接弄到最大 9 return oldTab;//达到最大限度 10 } 11 else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && 12 oldCap >= DEFAULT_INITIAL_CAPACITY) 13 newThr = oldThr << 1; // 扩容2倍 14 } 15 else if (oldThr > 0) // 初始容量置于阀值 16 newCap = oldThr; 17 else { //如果是0表示使用的默认值 18 newCap = DEFAULT_INITIAL_CAPACITY; 19 newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); 20 } 21 if (newThr == 0) {//得到新的阀值 22 float ft = (float)newCap * loadFactor; 23 newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? 24 (int)ft : Integer.MAX_VALUE); 25 } 26 threshold = newThr; 27 @SuppressWarnings({"rawtypes","unchecked"}) 28 Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];//新的空的表 29 table = newTab; 30 if (oldTab != null) { 31 for (int j = 0; j < oldCap; ++j) { 32 Node<K,V> e; 33 if ((e = oldTab[j]) != null) { 34 //获得该节点并置空原节点 35 oldTab[j] = null; 36 if (e.next == null) 37 //重新计算位置并放入 38 newTab[e.hash & (newCap - 1)] = e; 39 else if (e instanceof TreeNode)//该节点是红黑树 40 ((TreeNode<K,V>)e).split(this, newTab, j, oldCap); 41 else { // 普通链表 42 Node<K,V> loHead = null, loTail = null; 43 Node<K,V> hiHead = null, hiTail = null; 44 Node<K,V> next; 45 do { 46 next = e.next; 47 if ((e.hash & oldCap) == 0) {//如果为0则原来的位置不是则原来的位置加一倍 48 if (loTail == null) 49 loHead = e; 50 else 51 loTail.next = e; 52 loTail = e; 53 } 54 else { 55 if (hiTail == null) 56 hiHead = e; 57 else 58 hiTail.next = e; 59 hiTail = e; 60 } 61 } while ((e = next) != null); 62 if (loTail != null) { 63 loTail.next = null; 64 newTab[j] = loHead; 65 } 66 if (hiTail != null) { 67 hiTail.next = null; 68 newTab[j + oldCap] = hiHead; 69 } 70 } 71 } 72 } 73 } 74 return newTab; 75 }
