面试题 java的HashMap

85 阅读3分钟

HashMap

HashMap的底层是由数组和链表组成的。在java8之后又增加红黑树作为第三种数据结构。HashMap的结构节点是以键值对的形式出现。

put、get过程:

put操作: 首先是当我们要对他心map增加内容的时候,会先经过哈希运算算出。在数组中的哪一个位置,然后接查看这个速度是否为空,如果为空,则直接将该接上层的节点放入,如果飞空的话,就会查看是否为链表,如果是链表结构的话就会有一次与这个内容的节点比较如果存在键值相等的话,都说明你之前已经插入了,直接更新就可以。当链表的长度超过八,并且数组的长度大于64的时候,就会将链表结构转化为红黑树结构来提高我们的效率。并且在红黑树的节点长度为6以下的时候就会退回链表。

get操作: 查询的时候与放入存放数据的过程类似,也是需要先计算哈希值,然后查看位置是否有需要嗯查找的变件的值,如果有的话就说明有这个数据都会返回结果,如果没有的会返回空。

红黑树变换和退回的节点值为8和6的原因。 我认为是为了防止红黑树变换过快而导致性能消耗过大,如果他退回的节点值为7的话,那么我们有这种场景就是频繁增删。一条数据这样子的话,如果正好卡在这个8和7的位置,那么增加一个它就会变成8,就会生成红黑树,删除一个就变成7,然后又退回链表,这样子的话,经常这样的操作对我们的性能消耗是非常的大。

哈希值的计算 :hashCode(key) & length(nums.length - 1) 即key值的hashcode值与数组长度减一的二进制码想与操作计算得出。阿西code的曲值是int类型,可以取到21个值。而内存不可能一次性存储这么多的数据,所以需要对哈希值进行取模运算。使用&而不是%这是因为 & 运算比 % 更加高效,并且当 b 为 2 的 n 次方时,存在下面这样一个公式。

a % b = a & (b-1)

这也正好解释了为什么 HashMap 的数组长度要取 2 的整次方。

因为(数组长度-1)正好相当于一个“低位掩码”——这个掩码的低位最好全是 1,这样 & 操作才有意义,否则结果就肯定是 0,那么 & 操作就没有意义了。

为什么哈希map的默认长度为16? 通过哈希值计算的公式我们可以知道。哈西之与速度的长度和hashcode的值有关系。如果哈希表的速度长度为16的话,那么其长度仅一就是15 15的二进制为四个一(1111)。四个一的运算,其结果正好是hashcode的值后四位,所以这种情况下hashcode的值可以均匀分布的话,那么我们插入的节点也可以均匀分布,减少了我们的计算量和计算压力。

hashmap的安全问题。

1、hashmap的在多线程情况下rehash存在形成环形链表的可能。主要是因为JAVA 7的版本中这对于新阶段的加入使用了头插法,而在JAVA 8之后改为了尾插法这个。可存在的问题就解决掉了。

2、多线程下 put 会导致元素丢失。一个线程put进入了而另一个线程不知情,put进了新值。

3、put 和 get 并发时会导致 get 到 null。线程 A 执行put时,因为元素个数超出阈值而出现扩容,线程B 此时执行get,有可能导致这个问题。