0
点赞
收藏
分享

微信扫一扫

Java面试题目还原_HashMap

程序员阿狸 2022-01-20 阅读 52

HashMap面试题

HashMap是什么?HashMap的数据结构。

HashMap是一个以键值对存储的数据结构,jdk1.8之前以数组加链表存储,1.8之后加入了红黑树,通过对键进行hash存储到对应的位置以及查找键对应的值。同时HashMap是一个非线程安全的数据结构。

线程不安全的具体情况。
  • jdk1.8之前采用头插法会形成环状链表导致不安全,1.8之前和之后都存在在put过程中当线程A定位到位置后时间片用完,线程B同样定位到该位置存入值后A再拿到资源再次存入B的位置,造成值覆盖的问题。
  • 同时代码中有++size的代码,会导致线程A先拿到size值后时间片用完,线程B再拿到Size加1写回主内存,此时A拿到size还是原来的size,加一后写回主内存,在这个过程中A、B都进行了+1的操作,但是size只增加了1。
什么时候转化成红黑树/链表?

当链表长度大于8且数组长度大于64时转化为红黑树。

static final int MIN_TREEIFY_CAPACITY = 64;//小于64扩容数组

if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
            resize();

转化为红黑树的原因

  • 第一个条件我认为是因为当链表长度大于8时查询的速度较慢,解决链化严重的问题,转化为红黑树可以极大地提高查询速度。
  • 第二个条件是因为当数组长度小于64时,数组中的元素不多,如果某条链表达到8,说明元素非常不平均,可以只靠扩大数组的容量来解决,因为扩容后会重新对元素进行定位,极大降低链表形成。

当红黑树长度小于6时,转化为链表

static final int UNTREEIFY_THRESHOLD = 6;
为什么是6时转化成链表?

防止频繁地转化浪费时间和空间。

刚才你有说到会进行扩容,那么如何扩容?什么时候扩容?

像上述情况链表大于8但数组长度小于64会进行扩容,以及达到数组长度*负载因子时会进行扩容。

扩容每次会将数组的长度扩大两倍,因为两倍方便计算hash时取模的运算。然后遍历旧数组中的元素,重新计算索引的位置放到新数组,然后用新数组替换掉旧数组。

初始长度和负载因子是多少呢?为什么这样设计?

负载因子初始为0.75,是基于空间和时间的平衡考虑,调高空间利用高但时间下降,调低相反。

static final float DEFAULT_LOAD_FACTOR = 0.75f;

初始长度为16,为2的4次方,扩容时同样为扩大两倍保证长度为2的n次幂。原因是在用Hash计算元素的索引位置时需要进行取模运算,而HashMap底层巧妙的地运用了与运算,当length为2的n次幂的时候 hash%length == hash&(length-1) ,与运算极高地提升了计算的速度,因为计算机底层为二进制的。

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
那具体的确认索引位置过程是怎样的的呢?

首先获取key的Hash,然后将获取的hash ^ (hash>>>16)进行运算。最后按上面的将得出的hash和length-1进行与运算。

static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);

高位运算目的是为了在数组的length较小的情况下,也能够保证高低位都参与到hash的计算。因为索引位置由hash确定,这样做可以减少发生hash冲突。

场景题:有固定数量的元素要存放到hashmap中,如果提高存储速度。

将初始大小设置为大于存放数量的2的n次方,且要大于length*负载因子(0.75),以减少扩容造成的时间消耗。不过这里hashmap会自动计算大于设置容量的第一个2的整数次幂的数。

HashMap的table什么时候创建?

table是采用懒加载的,在第一次put元素时才进行初始化。

if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;//resize方法中进行初始化
举报

相关推荐

0 条评论