JDK1.8 HashMap运行原理
一,什么是HashMap
- HashMap是桶加链表结构。
- 非synchronized,效率高。
- 允许空key,但又因为key不能重复,故同一个HashMap中只会有一个空key
二,put方法 - 当我们选择put一个key与value时,程序会首先对key进行hashCode,然后再对hashCode进行异或(^)变成hash,也就是经常说得扰乱,作用是减少hashCode相同产生的碰撞
如图:
- 当hash与桶的hash相同时进行qut,如有此桶里面有值,则将新值放到前一个值的next中,依次类推。但是桶下面的链表只能拥有最多8个元素,如超过8个元素将采用红黑树的方式进行表达元素顺序,采用红黑树的目的是为了防止链表太长,会影响获取key时间,当红黑树的长度小于8时,便将红黑树转换为链表,在元素8以下的时候,链表的效率高于红黑树。
3.见博客上说hashMap的加载因子默认为0.75,初始长度为16,当map长度大于长度乘以加载因子时扩容为原长度的一倍。这是对的,但看源码发现有一种特殊的情况.
例如:当只有8或9个的元素,但是这几个元素的hash相同,全部在一条链表上,当列表长度大于等于8时,会调用如下方法:
//此方法作用是将链表转换为红黑树
final void treeifyBin(Node<K,V>[] tab, int hash) {
int n, index; Node<K,V> e;
//MIN_TREEIFY_CAPACITY==64,tab.length为当前map长度
if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
resize();
else if ((e = tab[index = (n - 1) & hash]) != null) {
TreeNode<K,V> hd = null, tl = null;
do {
TreeNode<K,V> p = replacementTreeNode(e, null);
if (tl == null)
hd = p;
else {
p.prev = tl;
tl.next = p;
}
tl = p;
} while ((e = e.next) != null);
if ((tab[index] = hd) != null)
hd.treeify(tab);
}
}
由此方法可以看出,当map的长度小于64时,会调用resize()方法,此方法为扩容(扩容时会重新计算hash值,进行重新放数据,这也挺消耗性能),从上面可以看出即使map的长度没有大于长度*加载因子的长度时也是会扩容的,也可以看出来链表转红黑树在map的长度大于64时才会进行。
get方法
首先是获取key的hash与map中的hash对比,如没有,返回null,如命中,则获取第一个元素,进行equlas,如不相同,判断元素中的next是否为空,如不为空继续判断,如不相同,继续判断,当有相同时进行返回,如无返回null。
也许大家会有一个疑问,便是为什么采用红黑树而不采用二叉树呢?
让我来巴拉一波,那是因为二叉树在某一种极端情况下,与链表是一样的,极端情况下指得是,当只有左子树或只有右子树时,此时二叉树的作用是与链表一样的,不方便访问,而红黑树被称为平衡二叉树,所以不会出现这种情况,对红黑树感兴趣的同学可以自行百度以下哦