关于hashMap源码阅读的几个问题以及解答

关于HashMap源码阅读的几个问题以及解答

问题

1.HashMap的数据结构是怎么样子的?

答:HashMap在jdk1.8之后是由数组、链表、红黑树构成的,在链表的长度大于8的时候会将链表转化为红黑树,如果小于6,那么红黑树又会转化为链表。

2.HashMap默认数组容量是多少?默认的负载因子是多少?负载因子有什么用?

答:HashMap默认数组容量为16,默认的负载因子为0.75,负载因子的存在会使HashMap在达到最大容量*负载因子的时候就调用resize方法开始扩容(扩容为原来的两倍,每次扩容之后会进行rehash)。

3.如果new HashMap<>(33),数组容量多大?

答:64,因为HashMap的最大容量一定是2的整数次幂,如果初始化的时候不是2的整数次幂,会调用tableSizeFor方法进行计算,使得容器的大小为传入值最接近的2的整数次幂(而且这个值是大于传入值的)。

4.HashMap的key的数组下标怎么算?

答:用的是key的hash算法与数组的大小减一做与运算,即 hash(key)&(n-1),n代表数组的大小。

5.HashMap 什么时候创建数组占用内存?数组下标碰撞怎么处理?

答:通过阅读源码可以知道,HashMap在初始化容量的时候并没有给底层的Hash数组Node[] table初始化,依旧为null,而是在第一次put元素的时候才会被初始化。
数组下标碰撞问题:就是常说的hash冲突问题,产生哈希冲突的原因可能有两个。(可以从数组下标产生的原理来看:hash(key)&(n-1))
第一:put的时候key重复
第二:hash算法的局限性,put的key虽然不同,但是经过hash算法后得出的hash值一样
HashMap是通过链表法来解决hash冲突的,当算出数组下标相同时,会形成链表的形式存放(当然,如果链表的长度大于8的时候,会转化为红黑树)。

6.HashMap什么时候扩容?

答:HashMap会在容量达到最大容量*负载因子的时候就会扩容。

7.HashMap为什么默认容量必须是2的幂数?

答:
1.因为数组的下标计算公式为hash(key)&(n-1)),如果容量为2的整数次幂,那么 (n-1)化为2进制的时候之前为 1 的位后面全是 1,这样就能保证 (n-1) & hash 后相应的位数既可能是 0 又可能是 1,这取决于 hash 的值。这样能保证散列的均匀,同时与运算效率高。
2.容量为2的整数次幂,可以减少hash冲突。

8.为什么HashMap是线程不安全的?

答:如果两个线程A与B,A先获得执行权,put值的时候算出数组的下标,获得链表头节点,但是还没有放入的时候,线程的执行权被B夺走了,B在put的时候也计算自己的数组下标,也获得链表头的节点,放入值,随后A线程放入值。如果这时候二者算出的数组下标索引是一样的,那么B的数据放在链表头节点,A也要放在链表头节点,这样B的数据就会被A覆盖,造成数据不一致,也就存在线程安全问题。(在多线程环境下可以使用Conllections工具类的Collections.synchronizedMap(hashMap)方法使hashMap变为线程安全)。

你可能感兴趣的:(关于hashMap源码阅读的几个问题以及解答)