关于HashMap

这个是网上查资料抄的,感谢百度

HashMap 底层的数据结构主要是:数组 + 链表 + 红黑树。其中当链表的长度大于等于 8 时,链表会转化成红黑树,当红黑树的大小小于等于 6 时,红黑树会转化成链表

在这里插入图片描述
点进源码看看
Treeify_threshold 为8 就是说节点大于等于8时,会判断是否要转为红黑树
min_treeify_capacity 就是 说 数组容量 大于64时,链表才会转化为红黑树
也就是说: 数组容量大于64,并且链表的节点个数大于8时候,就会把链表改为红黑树

好问题来了:

JDK1.8中HashMap在出现hash碰撞时链表长度超过8就一定会变成红黑树吗

答案是:否.

实际上转换红黑树有个大前提,就是当前hash
table的长度也就是HashMap的capacity(不是size)不能小于64.小于64就只是做个扩容.

Treeify_threshold设置为8是有原因的:
还是点进 Hashmap的源码来看,里面的红黑树节点:
在这里插入图片描述

看一下链表的节点:

在这里插入图片描述
网上说 TreeNodes 占用空间是 Nodes的两倍,不管对不对,反正红黑树要更加耗费空间,正常情况下是一般不要用红黑树,当我的Entry数组大于 64了,然而链表的节点个数还是大于8,说明一定是hash算法出现问题了,出现了严重的hash碰撞了,所有有必要使用红黑树,将长链表查询O(N) 的复杂度降低为 O(logN)

Treeify_threshold设置为 8是参考了泊松分布

数量 概率
0 : 0.60653066
1 : 0.30326533
2 : 0.07581633
3 : 0.01263606
4 : 0.00157952
5: 0.00015795
6: 0.00001316
7: 0.00000094
8: 0.00000006

按照泊松分布计算,理论上来说,链表长度为8 的概率是 0.00000006,属于小概率事件,正常来说不会转红黑树

负载因子:

负载因子 = 总键值对数 / 箱子数量

哈希表还有 一个重要的属性:负载因子,它是衡量哈希表的空/满程度,一定程度上也能体现查询的效率。其计算公式为:
当负载因子大于 0.75时,就会扩容

负载因子越大,意味着哈希表越满,越容易导致冲突(更大的概念找到同一个箱子上),因而查询效率也就更低。因而,一般来说,当负载因子大于某个常数(可能是1,也可能是其他值,Java8的HashMap的负载因子为0.75)时,哈希表就会自动扩容。

一篇不错的文章

关于 Hashmap 红黑树 的查找的源码

  /**
         * Finds the node starting at root p with the given hash and key.
         * The kc argument caches comparableClassFor(key) upon first use
         * comparing keys.
         */
        final TreeNode<K,V> find(int h, Object k, Class<?> kc) {
            TreeNode<K,V> p = this;
            do {
                int ph, dir; K pk;
                TreeNode<K,V> pl = p.left, pr = p.right, q;
                if ((ph = p.hash) > h)
                    p = pl;
                else if (ph < h)
                    p = pr;
                else if ((pk = p.key) == k || (k != null && k.equals(pk)))
                    return p;
                else if (pl == null)
                    p = pr;
                else if (pr == null)
                    p = pl;
                else if ((kc != null ||
                          (kc = comparableClassFor(k)) != null) &&
                         (dir = compareComparables(kc, k, pk)) != 0)
                    p = (dir < 0) ? pl : pr;
                else if ((q = pr.find(h, k, kc)) != null)
                    return q;
                else
                    p = pl;
            } while (p != null);
            return null;
        }

 /**
     * Implements Map.get and related methods.
     *
     * @param hash hash for key
     * @param key the key
     * @return the node, or null if none
     */
    final Node<K,V> getNode(int hash, Object key) {
        Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & hash]) != null) {
            if (first.hash == hash && // always check first node
                ((k = first.key) == key || (key != null && key.equals(k))))
                return first;
            if ((e = first.next) != null) {
                if (first instanceof TreeNode)
                    return ((TreeNode<K,V>)first).getTreeNode(hash, key);
                do {
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        return e;
                } while ((e = e.next) != null);
            }
        }
        return null;
    }

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值