hashMap底层实现
程序员文章站
2024-03-12 15:50:14
...
1 hashmap的数据结构是什么样子的?
jdk 1.8 新加的内容
- 当链表长度大于8之后(在数组长度大于64的前提下),链表就会转换为红黑树了。
2 put方法怎么实现的?
3 扩容机制是什么?【jdk 1.7的代码,1.8的加入了红黑树,代码比较难看】
4 为什么扩容大小为2的幂次方?
查找快:将Key值进行Hash后得到Hash值,Hash值得范围大概40亿,很明显,内存存不下,进行转换,对数组的长度(即hashmap的长度)进行取模运算,得到的余数才用来做要存放的位置(即对应的数组下标)。[这也解释了HashMap的长度为什么是2的幂次方,为了提高取模运算的效率]。
分散均匀:
HashMap 采用这种非常规设计,主要是为了在取模和扩容时做优化,同时为了减少hash碰撞,HashMap 定位哈希桶索引位置时,也加入了高位参与运算的过程。
长度16或者其他2的幂,Length-1的值是所有二进制位全为1,这种情况下,index的结果等同于HashCode后几位的值。只要输入的HashCode本身分布均匀,Hash算法的结果就是均匀的。
如果不为16或者2的幂,那么,经过hash算法之后,有些index结果出现的概率会更大,而有些index则永远步会出现。即不符合hash算法的j均匀分布的原则。
5 什么是负载因子,为什么默认值是0.75?
负载因子表示的是一个散列表的空间使用程度。
源码里对负载因子有一段解释,大概意思就是:在理想情况下,使用随机哈希吗,节点出现的频率在hash桶中遵循泊松分布,同时给出了桶中元素的个数和概率的对照表。
从上表可以看出当桶中元素到达8个的时候,概率已经变得非常小,也就是说用0.75作为负载因子,每个碰撞位置的链表长度超过8个是几乎不可能的。
hash容器指定初始容量尽量为2的幂次方。
HashMap负载因子为0.75是空间和时间成本的一种折中。
6 如何解决 hash 冲突的?
拉链法
7 是不是线程安全的?
- HashMap不是线程安全的,在多线程并发的环境下,可能会产生死锁等问题。
- 补充一点 Hashtable 是线程安全的。还有并发的 HashMap :ConcurrentHashMap(java5引入的)。
8 hashmap 与 hashtable 的与别是什么?
1. HashMap线程不安全,是非synchronized的,Hashtable是线程安全的。
2. HashMap可以接受null(HashMap可以接受为null的键值(key)和值(value)),而Hashtable则不行。
3. 是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并不是一个一定发生的行为,要看JVM。这条同样也是Enumeration和Iterator的区别。
4. 由于Hashtable是线程安全的也是synchronized,所以在单线程环境下它比HashMap要慢。如果你不需要同步,只需要单一线程,那么使用HashMap性能要好过Hashtable。
5. HashMap不能保证随着时间的推移Map中的元素次序是不变的。
6. 继承的父类不同、作者不同,产生时间不同。HashMap是继承自AbstractMap类,而HashTable是继承自Dictionary类。不过它们都实现了同时实现了Map、Cloneable(可复制)、Serializable(可序列化)这三个接口
7. 初始容量大小和每次扩充容量大小的不同。Hashtable默认的初始大小为11,之后每次扩充,容量变为原来的2n+1。HashMap默认的初始化大小为16。之后每次扩充,容量变为原来的2倍。
8. 计算hash值的方法不同。为了得到元素的位置,首先需要根据元素的Key计算出一个hash值,然后再用这个hash值来计算得到最终的位置。Hashtable直接使用对象的hashCode。然后再使用取余来获得最终的位置,比较耗时。HashMap是通过位运算来进行操作的,效率高。
下面是 Hashtable 的确定索引方法:
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
9 为什么不一下子把链表转化成红黑树?
1. 构造红黑树要比构造链表复杂,在链表的节点不多的时候,从整体的性能看来, 数组+链表+红黑树的结构可能不一定比数组+链表的结构性能高。
2. HashMap 频繁的扩容,会造成底部红黑树不断的进行拆分和重组,这是非常耗时的。因此,也就是链表长度比较长的时候转变成红黑树才会显著提高效率。
上一篇: 中序线索二叉树
下一篇: 网易雷火2020秋招平台开发笔试-编程题