zoukankan      html  css  js  c++  java
  • 深入理解JDK8中的HashMap

      一、首先看一下HashMap的数据结构(数组+链表/红黑树),如下图:

      1、红黑树特性(缺一不可):

    (1)、每个节点要么是红色要么是黑色。

    (2)、根节点是黑色。

    (3)、所有叶子节点都是黑色(叶子节点为NIL或者NULL节点)。

    (4)、不存在两个连续的红色节点。

    (5)、任意节点(包含跟节点)到其叶子节点的所有路径都包含相同数目的黑色节点。

      2、为什么HashMap中使用红黑树而不使用AVL树呢?

    红黑树被称为弱AVL树,牺牲了严格的高度平衡的优越条件为代价(红黑树左右子树的高度差不超过一倍即可)使其能够以O(log2 n)的时间复杂度进行搜索、插入、删除操作;此外,由于它的设计,任何不平衡都会在三次旋转之内解决。因为HashMap的使用场景中插入和删除操作是非常频繁的,所以在HashMap中使用了红黑树。

      3、红黑树RBT与平衡二叉树AVL比较:

    (1)、红黑树和AVL树类似,都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能。

    (2)、红黑树和AVL树的区别在于它使用颜色来标识节点的高度,它所追求的是局部平衡而不是AVL树中的非常严格的平衡。

    (3)、AVL 树比红黑树更加平衡,但AVL树在插入和删除的时候也会存在大量的旋转操作。所以当你的应用涉及到频繁的插入和删除操作,切记放弃AVL树,选择性能更好的红黑树;当然,如果你的应用中涉及的插入和删除操作并不频繁,而是查找操作相对更频繁,那么就优先选择 AVL 树进行实现。

       二、HashMap元素插入过程及一些参数的详解

    1、首先,需要了解HashMap源码中几个重要的参数:

    DEFAULT_INITIAL_CAPACITY:默认初始化大小

    MAXIMUM_CAPACITY:最大容量

    DEFAULT_LOAD_FACTOR:默认的负载因子

    TREEIFY_THRESHOLD:链表转化为红黑树的阈值(包含)

    UNTREEIFY_THRESHOLD:红黑树转化为链表的阈值(包含)

    MIN_TREEIFY_CAPACITY:当数组大小小于该值时,不进行链表向红黑树的转化,而是进行扩容

    2、HashMap存储元素过程:

     (1)图中刚开始有计算 key 的 hash 值的设计?

    拿到 key 的 hashCode,并将 hashCode 的高16位和 hashCode 进行异或(XOR)运算,得到最终的 hash 值。

     (2)为什么要将 hashCode 的高16位参与运算?

    主要是为了在 table 的长度较小的时候,让高位也参与运算,并且不会有太大的开销。

    (3)为什么链表转红黑树的阈值是8?

    我们平时在进行方案设计时,必须考虑的两个很重要的因素是:时间和空间。对于 HashMap 也是同样的道理,简单来说,阈值为8是在时间和空间上权衡的结果。红黑树节点大小约为链表节点的2倍,在节点太少时,红黑树的查找性能优势并不明显,付出2倍空间的代价不值得。理想情况下,使用随机的哈希码,节点分布在 hash 桶中的频率遵循泊松分布,按照泊松分布的公式计算,链表中节点个数为8时的概率为 0.00000006,这个概率足够低了,并且到8个节点时,红黑树的性能优势也会开始展现出来,因此8是一个较合理的数字。

    (4)HashMap 的默认初始容量是多少?HashMap 的容量有什么限制吗?

    默认初始容量是16。HashMap 的容量必须是2的N次方,HashMap 会根据我们传入的容量计算一个大于等于该容量的最小的2的N次方,例如传 9,容量为16。

     (5)为什么 HashMap 的容量必须是 2 的 N 次方?

    计算索引位置的公式为:(n - 1) & hash,当 n 为 2 的 N 次方时,n - 1 为低位全是 1 的值,此时任何值跟 n - 1 进行 & 运算的结果为该值的低 N 位,达到了和取模同样的效果,实现了均匀分布。实际上,这个设计就是基于公式:x mod 2^n = x & (2^n - 1),因为 & 运算比 mod 具有更高的效率。当 n 不为 2 的 N 次方时,hash 冲突的概率明显增大。

    (6)为什么HashMap的负载因子默认为0.75?

     在HashMap的类注释上有如图一段解释:大致意思是说负载因子是0.75的时候,空间利用率比较高,而且避免了相当多的Hash冲突,使得底层的链表或者是红黑树的高度比较低,提升了空间效率。

  • 相关阅读:
    JavaScriptSerializer的使用, 今上午琢磨了半天, 小结一下.
    解决TextBox中, JS方法(DatePicker)改变Text内容后, 无法触发OnTextChanged事件的问题
    学习笔记Oracle操作总结
    学习笔记再次理解事件和委托
    Linux第一天接触, 安装CentOS后解决中文字体的问题
    jQuery验证客户端控件, 在提交表单时用MD5.js将密码变成密文
    学习笔记 缓存、动态页面静态化、网站优化
    学习笔记母板页、用户控件、第三方控件及视图状态管理
    学习笔记Javascript原型对象、this的5钟用法、原型继承、Caller和Callee的使用
    学习笔记元数据、程序集、GAC版本控制、属性(Attribute)、反射(利用.NET编译器实现表达式计算器)
  • 原文地址:https://www.cnblogs.com/fuxuepan/p/14409901.html
Copyright © 2011-2022 走看看