zoukankan      html  css  js  c++  java
  • 阿里面试题:为什么Map桶中个数超过8才转为红黑树

    为什么一个是8一个是6:防止频繁来回转换小消耗性能

    这是笔者面试阿里时,被问及的一个问题,应该不少人看到这个问题都会一面懵逼。因为,大部分的文章都是分析链表是怎么转换成红黑树的,但是并没有说明为什么当链表长度为8的时候才做转换动作。笔者第一反应也是一样,只能初略的猜测是因为时间和空间的权衡。

    要弄明白这个问题,我们首先要明白为什么要转换,这个问题比较简单,因为Map中桶的元素初始化是链表保存的,其查找性能是O(n),而树结构能将查找性能提升到O(log(n))。当链表长度很小的时候,即使遍历,速度也非常快,但是当链表长度不断变长,肯定会对查询性能有一定的影响,所以才需要转成树。至于为什么阈值是8,我想,去源码中找寻答案应该是最可靠的途径。

    8这个阈值定义在HashMap中,如下所示,这段注释只说明了8是bin(bin就是bucket,即HashMap中hashCode值一样的元素保存的地方)从链表转成树的阈值,但是并没有说明为什么是8:

    复制代码
    1 /**
    2  * The bin count threshold for using a tree rather than list for a
    3  * bin.  Bins are converted to trees when adding an element to a
    4  * bin with at least this many nodes. The value must be greater
    5  * than 2 and should be at least 8 to mesh with assumptions in
    6  * tree removal about conversion back to plain bins upon shrinkage.
    7  */
    8 static final int TREEIFY_THRESHOLD = 8;
    复制代码

    我们继续往下看,在HashMap中有一段Implementation notes,笔者摘录了几段重要的描述,第一段如下所示,大概含义是当bin变得很大的时候,就会被转换成TreeNodes中的bin,其结构和TreeMap相似,也就是红黑树:

    This map usually acts as a binned (bucketed) hash table, but
    when bins get too large, they are transformed into bins of TreeNodes,
    each structured similarly to those in java.util.TreeMap

    继续往下看,TreeNodes占用空间是普通Nodes的两倍,所以只有当bin包含足够多的节点时才会转成TreeNodes,而是否足够多就是由TREEIFY_THRESHOLD的值决定的。当bin中节点数变少时,又会转成普通的bin。并且我们查看源码的时候发现,链表长度达到8就转成红黑树,当长度降到6就转成普通bin。

    这样就解析了为什么不是一开始就将其转换为TreeNodes,而是需要一定节点数才转为TreeNodes,说白了就是trade-off,空间和时间的权衡

    复制代码
     1 Because TreeNodes are about twice the size of regular nodes, we
     2 use them only when bins contain enough nodes to warrant use
     3 (see TREEIFY_THRESHOLD). And when they become too small (due to
     4 removal or resizing) they are converted back to plain bins.  In
     5 usages with well-distributed user hashCodes, tree bins are
     6 rarely used.  Ideally, under random hashCodes, the frequency of
     7 nodes in bins follows a Poisson distribution
     8 (http://en.wikipedia.org/wiki/Poisson_distribution) with a
     9 parameter of about 0.5 on average for the default resizing
    10 threshold of 0.75, although with a large variance because of
    11 resizing granularity. Ignoring variance, the expected
    12 occurrences of list size k are (exp(-0.5)*pow(0.5, k)/factorial(k)). 
    13 The first values are:
    14 0:    0.60653066
    15 1:    0.30326533
    16 2:    0.07581633
    17 3:    0.01263606
    18 4:    0.00157952
    19 5:    0.00015795
    20 6:    0.00001316
    21 7:    0.00000094
    22 8:    0.00000006
    23 more: less than 1 in ten million
    复制代码

    这段内容还说到:当hashCode离散性很好的时候,树型bin用到的概率非常小,因为数据均匀分布在每个bin中,几乎不会有bin中链表长度会达到阈值。但是在随机hashCode下,离散性可能会变差,然而JDK又不能阻止用户实现这种不好的hash算法,因此就可能导致不均匀的数据分布。不过理想情况下随机hashCode算法下所有bin中节点的分布频率会遵循泊松分布,我们可以看到,一个bin中链表长度达到8个元素的概率为0.00000006,几乎是不可能事件。所以,之所以选择8,不是拍拍屁股决定的,而是根据概率统计决定的。由此可见,发展30年的Java每一项改动和优化都是非常严谨和科学的。

    • 画外音

    笔者通过搜索引擎搜索这个问题,发现很多下面这个答案(猜测也是相互转发):

    红黑树的平均查找长度是log(n),如果长度为8,平均查找长度为log(8)=3,链表的平均查找长度为n/2,当长度为8时,平均查找长度为8/2=4,这才有转换成树的必要;链表长度如果是小于等于6,6/2=3,而log(6)=2.6,虽然速度也很快的,但是转化为树结构和生成树的时间并不会太短。

    笔者认为这个答案不够严谨:“3相比4有转换的必要,而2.6相比3就没有转换的必要?

     所以我认为答这个题从下面3点:

    1.TreeNodes占用空间是普通Nodes的两倍,为了空间和时间的权衡,为6时红黑树也比链表快,但转换过程消耗和空间消耗不划算

    2.节点的分布频率会遵循泊松分布,链表长度达到8个元素的概率为0.00000006,几乎是不可能事件

    3.提出来回转化的阈值8和6阈值为什么不一样

    至于为什么转化为红黑树的阈值8和转化为链表的阈值6不一样,是为了避免频繁来回转化

    https://www.cnblogs.com/linghu-java/p/10598758.html

  • 相关阅读:
    c语言结构体数组引用
    c语言结构体数组定义的三种方式
    如何为SAP WebIDE开发扩展(Extension),并部署到SAP云平台上
    SAP SRM ABAP Webdynpro和CFCA usb key集成的一个原型开发
    使用SAP API portal进行SAP SuccessFactors的API测试
    SAP UI5应用里的页面路由处理
    在SAP WebIDE Database Explorer里操作hdi实例
    如何使用SAP事务码SAT进行UI应用的性能分析
    使用SAP WebIDE进行SAP Cloud Platform Business Application开发
    SAP CRM WebClient UI ON_NEW_FOCUS的用途
  • 原文地址:https://www.cnblogs.com/twoheads/p/10667449.html
Copyright © 2011-2022 走看看