阿里面试题:为什么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
阿里面试题:为什么Map桶中个数超过8才转为红黑树的更多相关文章
- 为什么Map桶中个数超过8才转为红黑树
这是笔者一个好友面试阿里时,被问及的一个问题,应该不少人看到这个问题都会一面懵逼.因为,大部分的文章都是分析链表是怎么转换成红黑树的,但是并没有说明为什么当链表长度为8的时候才做转换动作.笔者第一反应 ...
- java中treemap和treeset实现(红黑树)
java中treemap和treeset实现(红黑树) TreeMap 的实现就是红黑树数据结构,也就说是一棵自平衡的排序二叉树,这样就可以保证当需要快速检索指定节点. TreeSet 和 Tre ...
- 在nginx启动后,如果我们要操作nginx,要怎么做呢 别增加无谓的上下文切换 异步非阻塞的方式来处理请求 worker的个数为cpu的核数 红黑树
nginx平台初探(100%) — Nginx开发从入门到精通 http://ten 众所周知,nginx性能高,而nginx的高性能与其架构是分不开的.那么nginx究竟是怎么样的呢?这一节我们先来 ...
- 【数据结构】27、红黑树,节点插入,修复平衡操作总结(针对jdk8中hashmap冲突过多链表转红黑树)
二叉树节点插入 0.如果只有一个节点,那么就直接作为根,涂黑,如果父为黑,或者祖父为空,那么不做操作 1.叔叔节点不为空且为红 那么就修改父,叔叔,祖父节点颜色,最后把当前节点设置为祖父节点,在进行平 ...
- Leetcode - 剑指offer 面试题29:数组中出现次数超过一半的数字及其变形(腾讯2015秋招 编程题4)
剑指offer 面试题29:数组中出现次数超过一半的数字 提交网址: http://www.nowcoder.com/practice/e8a1b01a2df14cb2b228b30ee6a92163 ...
- 用#define来实现多份近似代码 - map,set中的应用
在stl中map,set内部都是使用相同的红黑树实现,map对应模板参数key_type,mapped_type,而set对应模板参数没有mapped_type 两者都支持insert操作 pair& ...
- stl vector、红黑树、set、multiset、map、multimap、迭代器失效、哈希表(hash_table)、hashset、hashmap、unordered_map、list
stl:即标准模板库,该库包含了诸多在计算机科学领域里所常用的基本数据结构和基本算法 六大组件: 容器.迭代器.算法.仿函数.空间配置器.迭代适配器 迭代器:迭代器(iterator)是一种抽象的设计 ...
- 剑指XX游戏(六) - 轻松搞定面试中的红黑树问题
原文地址 http://blog.csdn.net/silangquan/article/details/18655795?utm_source=tuicool&utm_medium=refe ...
- 红黑树规则,TreeSet原理,HashSet特点,什么是哈希值,HashSet底层原理,Map集合特点,Map集合遍历方法
==学习目标== 1.能够了解红黑树 2.能够掌握HashSet集合的特点以及使用(特点以及使用,哈希表数据结构) 3.能够掌握Map集合的特点以及使用(特点,常见方法,Map集合的遍历) 4.能够掌 ...
随机推荐
- hiho1259 A Math Problem (数位dp)
题目链接:http://hihocoder.com/problemset/problem/1259 题目大意:g(t)=(f(i)%k=t)的f(i)的个数 求所有的(0-k-1)的g(i)的异或总值 ...
- python学习日记(生成器函数进阶)
迭代器和生成器的概念 迭代器 对于list.string.tuple.dict等这些容器对象,使用for循环遍历是很方便的.在后台for语句对容器对象调用iter()函数.iter()是python内 ...
- 【比赛】NOIP2018 旅行
发现 \(m\) 只有两种取值,于是可做了 树的直接贪心 图的枚举环上的边去掉,然后做树的贪心,搜的时候剪一下枝吧 写得有点乱 #include<bits/stdc++.h> #defin ...
- Educational Codeforces Round 33 (Rated for Div. 2) F. Subtree Minimum Query(主席树合并)
题意 给定一棵 \(n\) 个点的带点权树,以 \(1\) 为根, \(m\) 次询问,每次询问给出两个值 \(p, k\) ,求以下值: \(p\) 的子树中距离 \(p \le k\) 的所有点权 ...
- 【HDU1846】Brave Game(博弈论)
题面 HDU 题解 \(Bash\ Game\)模板题 #include<iostream> using namespace std; int T,n,m; int main() { io ...
- 洛谷 P3121 【[USACO15FEB]审查(黄金)Censoring (Gold)】
被自己学校OJ的毒瘤测评姬卡到自闭 Hash+栈+优化暴力 其实思路也很简单,就是把单词存进一个结构体,记录其哈希值和长度,然后就可以开始匹配了 但是,理论复杂度很高,为\(O(n*length)\) ...
- Weblate 2.11安装配置文档
一.系统环境: OS:CentOS 6.8 x64 Minimal HostName:Weblate IP:192.168.75.153 Python:2.7.13 pip:9.0.1 Weblate ...
- react-native中的请求数据
很多移动应用都需要从远程地址中获取数据或资源.你可能需要给某个 REST API 发起 POST 请求以提交用户数据,又或者可能仅仅需要从某个服务器上获取一些静态内容. 使用 Fetch React ...
- 第三十六篇-FloatingActionButton的使用
效果图: 准备两张图片,一张作为桌面背景,一张作为那个悬浮的加号.放在mipmap下面. 首先,添加Imageview作为桌面背景,并设置扩充整个屏幕.接着,添加一个悬浮按钮,放在右下角,可以对悬浮按 ...
- 第三十节,目标检测算法之Fast R-CNN算法详解
Girshick, Ross. “Fast r-cnn.” Proceedings of the IEEE International Conference on Computer Vision. 2 ...