本文主要记录阅读HashMap源码的过程
1、HashMap的JavaDoc
HashMap类位于JDK的java.util包
总体思路:
每一个key,value对,以node对象存放在连续的数组中,hash值相同的key以链表的形式存放在同一个index处(jdk8之后,如果同一个index下的node节点数大于8时,将以红黑树的形式存放,这样将提高查询效率)。
2、类图
JDK1.8
成员变量
大写的成员变量都是final
DEFAULT_INITIAL_CAPACITY:默认容量,必须是2的幂,初始值为16
MAXIMUM_CAPACITY:最大容量,2的30次方
DEFAULT_LOAD_FACTOR:加载因子,0.75(不知道是为什么取值为0.75)
TREEIFY_THRESHOLD:阈值8(这是jdk8之后才添加的,表示hashmap中某个桶中的节点数大于该值时,存储结构将由原来的链表结构变为红黑树结构)
Node<K,V> 存放在数组中的节点类型;
3、关键操作
对象构造
注意:所有的代码都来自于jdk1.8
注意:在构造hashmap对象时,不会为table分配内存,table的内存分配在put操作时进行
1、不指定任何参数,此时所有属性均按照初始值;
2、构造方法指定map的大小m,并不一定会分配m大小的内存给table,因为capacity必须是2的幂,需要通过如下代码设置
1 static final int tableSizeFor(int cap) { 2 3 int n = cap -1; 4 5 n |= n >>>1; 6 7 n |= n >>>2; 8 9 n |= n >>>4; 10 11 n |= n >>>8; 12 13 n |= n >>>16; 14 15 return (n <0) ?1 : (n >=MAXIMUM_CAPACITY) ?MAXIMUM_CAPACITY : n +1; 16 17 }
hash值计算
static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }
将key的hashcode的高16位和低16位做异或操作,按照代码中的注释解释说是为了减小hash冲突。
put操作
调用putVal,代码如下:
代码注释是自己添加
hashmap的桶中元素可能是Node,或者treenode,如果元素较少,则使用链表存储,如果元素较多,则改为红黑树存储
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node<K,V>[] tab; Node<K,V> p; int n, i; if ((tab = table) == null || (n = tab.length) == 0)// 如果是第一次put操作,table为null,则为其分配默认capacity大小的空间,resize返回的值一定是2的幂 n = (tab = resize()).length; if ((p = tab[i = (n - 1) & hash]) == null)//n-1的二进制是全为1,==null表示该桶为空,如果不为空,把桶中链表第一个元素(或者红黑树root) tab[i] = newNode(hash, key, value, null);//桶中第一个元素,采用链表形式,因此用newNode else {//桶中已经有其他元素,hash碰撞 Node<K,V> e; K k; if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))//如果put的key已经存在 e = p; else if (p instanceof TreeNode)//如果p是红黑树节点,则插入一个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) // -1 for 1st,当桶中元素大于阈值时 treeifyBin(tab, hash);//将链表结构转换为树结构 break; } if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))//如果遍历过程中,某个元素的key与需要put的key一样则break break; p = e; } } if (e != null) { // existing mapping for key V oldValue = e.value; if (!onlyIfAbsent || oldValue == null)//为节点赋值 e.value = value; afterNodeAccess(e);//不知为何该方法为空 return oldValue; } } ++modCount;//修改次数计数+1,执行到这里表示put的key是最新的,不然上面的if就已经返回了return oldValue; if (++size > threshold)//如果hashmap的总元素大于阈值,则扩容 resize(); afterNodeInsertion(evict); return null; }
扩容操作reSize
reSize除了扩展空间之外,还需要将原有的数据转移到新申请的内存空间
final Node<K,V>[] resize() { Node<K,V>[] oldTab = table; int oldCap = (oldTab == null) ? 0 : oldTab.length;//存放原有的capacity 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; // double threshold } else if (oldThr > 0) // initial capacity was placed in threshold newCap = oldThr; else { // zero initial threshold signifies using defaults 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)//原有的hashmap中,该桶只有一个元素 newTab[e.hash & (newCap - 1)] = e; else if (e instanceof TreeNode)//如果该桶中元素是树节点, ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);//把树节点拆分到新map中的不同桶中 else { // preserve order 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; }
get操作:这个方法相对简单,不做过多阅读
remove操作
public V remove(Object key) { Node<K,V> e; return (e = removeNode(hash(key), key, null, false, true)) == null ? null : e.value; }
调用removeNode方法
final Node<K,V> removeNode(int hash, Object key, Object value, boolean matchValue, boolean movable) { Node<K,V>[] tab; Node<K,V> p; int n, index; if ((tab = table) != null && (n = tab.length) > 0 && (p = tab[index = (n - 1) & hash]) != null) {//首先需要remove的节点必须存在,否则返回null Node<K,V> node = null, e; K k; V v; if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) node = p; else if ((e = p.next) != null) { if (p instanceof TreeNode) node = ((TreeNode<K,V>)p).getTreeNode(hash, key); else { do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) { node = e; break; } p = e; } while ((e = e.next) != null); } } if (node != null && (!matchValue || (v = node.value) == value || (value != null && value.equals(v)))) { if (node instanceof TreeNode) ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable); else if (node == p) tab[index] = node.next; else p.next = node.next; ++modCount; --size; afterNodeRemoval(node); return node; } } return null; }