说明
- 底层采用数组+链表的数据结构存储
- 默认初始16容量,每次扩容2倍,加载因子0.75(即到了最大容量的0.75倍就开始扩容,因为过大的时候hash碰撞严重
- 自定义容量会按照向上取最近2^n数量定义初始容量,如
new HashMap(6)
,实际初始容量为8
- 初始化时并不会直接分配数组空间,而会在第一次put的时候初始化数组
- 当链表长度超过8时,会在插入数据之后马上执行数组转红黑树(当数组长度不超过64的时候,链表长度超过8会先扩容而不会链表转红黑树)
- 扩容主要是扩容数组的长度(底层为数据的均涉及到扩容,因为数组是连续不可变的)
源码解析
Jdk8源代码
我们直接看put()
方法就大致可以知道HashMap的原理了
方法的执行流程大致分为
- 计算Hash,并执行高地位异或得到均匀Hash值
- 判断是否未初始化,执行初始化
- hash取模得到数组位置
- 插入数据
- 判断数组是否有值,没有值直接插入
- 判断子节点是否有值,没有直接插入
- 判断子节点是链表还是红黑树,
- 红黑树:按照红黑树插入,
- 链表
- 通过while循环判断子节点是否有key、是否匹配当前key、是否到了末尾插入
- 判断是否需要转红黑树
- (红黑树链表插入)
- 判断是否需要扩容
计算Hash
1 2 3 4
| static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }
|
- 得到通过h=key.hashCode()方法计算key的hashcode的值,
- h >>> 16
通过计算出的hashcode的值向右移动16位,使原有的hashcode的值的高16位变低16位,高16位则统一都变为0。这样做的好处是,可以将hashcode高位和低位的值进行混合做异或运算,而且混合后,低位的信息中加入了高位的信息,这样高位的信息被变相的保留了下来。掺杂的元素多了,那么生成的hash值的随机性会增大。
异或运算:(h = key.hashCode()) ^ (h >>> 16)
说明 |
2进制 |
原来的hashCode |
1111 1111 1111 1111 0100 1100 0000 1010 |
移位后的hashCode |
0000 0000 0000 0000 1111 1111 1111 1111 |
进行异或运算结果 |
1111 1111 1111 1111 1011 0011 1111 0101 |
put 操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
|
public V put(K key, V value) { return putVal(hash(key), key, value, false, true); }
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node<K,V>[] tab; Node<K,V> p; int n; int i; if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize() ).length; if ((p = tab[i = (n - 1) & hash] ) == null) tab[i] = newNode(hash, key, value, null);
else { Node<K,V> e; K k; if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; else if (p instanceof TreeNode) e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); else { for (int binCount = 0; ; ++binCount) { if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); if (binCount >= TREEIFY_THRESHOLD - 1) treeifyBin(tab, hash); break; } if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; } } if (e != null) { V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; } } ++modCount; if (++size > threshold) resize(); afterNodeInsertion(evict); return null; }
|
resize()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
| final Node<K, V>[] resize() { Node<K, V>[] oldTab = table; int oldCap = (oldTab == null) ? 0 : oldTab.length; int oldThr = threshold; int newCap, newThr = 0; if (oldCap > 0) { if (oldCap >= MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return oldTab; } else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY) newThr = oldThr << 1; } else if (oldThr > 0) newCap = oldThr; else { newCap = DEFAULT_INITIAL_CAPACITY; newThr = (int) (DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); } if (newThr == 0) { float ft = (float) newCap * loadFactor; newThr = (newCap < MAXIMUM_CAPACITY && ft < (float) MAXIMUM_CAPACITY ? (int) ft : Integer.MAX_VALUE); } threshold = newThr; @SuppressWarnings({"rawtypes", "unchecked"}) Node<K, V>[] newTab = (Node<K, V>[]) new Node[newCap]; table = newTab; if (oldTab != null) { for (int j = 0; j < oldCap; ++j) { Node<K, V> e; if ((e = oldTab[j]) != null) { oldTab[j] = null; if (e.next == null) newTab[e.hash & (newCap - 1)] = e; else if (e instanceof TreeNode) ((TreeNode<K, V>) e).split(this, newTab, j, oldCap); else { Node<K, V> loHead = null, loTail = null; Node<K, V> hiHead = null, hiTail = null; Node<K, V> next; do { next = e.next; if ((e.hash & oldCap) == 0) { if (loTail == null) loHead = e; else loTail.next = e; loTail = e; } else { if (hiTail == null) hiHead = e; else hiTail.next = e; hiTail = e; } } while ((e = next) != null); if (loTail != null) { loTail.next = null; newTab[j] = loHead; } if (hiTail != null) { hiTail.next = null; newTab[j + oldCap] = hiHead; } } } } } return newTab; }
|