首页 文章详情

面试跳槽季,你了解HashMap1.8做了哪些优化?

proginn0937458772 | 69 2024-01-16 07:49 0 0 0
UniSMS (合一短信)

再谈HashMap






hashMap优化说明





本文主要针对jdk中hashMap从1.7=>1.8做了哪些优化说明,在阅读本文之前需要对hashMap原理有基本的了解






1.8HashMap做了哪些优化?





01


数据结构优化

在1.7时Map的结构为数组+链表,1.8为了解决hash冲突时导致链表的长度过长,针对链表进行了优化,引入了红黑树,1.8后的map结构为数组+链表/红黑树

02


hash函数优化

1.7需要对hashCode值进行4次移位运算,4次异或运算


1.7hash函数

                  final int hash(Object k) {
    int h = hashSeed;
    if (0 != h && k instanceof String) {
        return sun.misc.Hashing.stringHash32((String) k);
    }

    h ^= k.hashCode();

    // This function ensures that hashCodes that differ only by
    // constant multiples at each bit position have a bounded
    // number of collisions (approximately 8 at default load factor).
    h ^= (h >>> 20) ^ (h >>> 12);
    return h ^ (h >>> 7) ^ (h >>> 4);


1.8hash函数

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

1.8后hash函数优化为获取key的hashCode值后进行右移16位再进行异或运算,这样很大程度上提高了性能问题


03


链表插入的顺序

链表的插入方式由头插法变为了尾插法,1.7中将新元素放到数组中,原始节点作为新节点的后置节点,1.8是变量链表,将元素放到链 最后


这样做是为了避免在多线程对map进行扩容时链表结构形成环形数据结构,造成死循环

04


扩容优化

在1.7扩容时会调用hash函数重新hash,把旧数组上的元素复制到新数组上,1.8后在扩容时采用更简单的判断,只需要把索引位置发生变化的元素复制到新数组上面


扩容的时候为什么1.8不用重新hash就可以确定元素在新数组上的位置?

因为扩容时每次都是对数组进行扩容原数组的2倍,通过二进制来看仅仅是计算数组下标位置高位多了个1。


例如: 计算数组下标(数组长度-1) & hash

数组长度由16扩容到32,16-1 对应二进制为0000 1111

32-1 对应二进制为0001 1111


在进行&运算时,1与任何数&都是它本身,如下图hashcode高位第4位为0和高位为1的情况:

当第4位为0时重新hash值不变,

当低位为1时重新hash比原数量大16,刚好大就数组的长度

2756a63dcffc0d644ffb8b3550c2a721.webp


05


扩容时元素拷贝


在1.7时是循环遍历数组元素,并将旧数组元素复制到新数组上面,

在1.7之后是通过一种高低链表的方式,不需要拷贝到新数组的元素放在低链表,需要拷贝到新数组的元素放在高链表

如源码:loHead,hiHead

                                        Node<K,V> loHead = null, loTail = null;
                        Node<K,V> hiHead = null, hiTail = null;
                        Node<K,V> next;
                        do {
                            next = e.next;
                            if ((e.hash & oldCap) == 0) {
                                if (loTail == null)
                                    loHead = e;
                                else
                                    loTail.next = e;
                                loTail = e;
                            }
                            else {
                                if (hiTail == null)
                                    hiHead = e;
                                else
                                    hiTail.next = e;
                                hiTail = e;
                            }
                        } while ((e = next) != null);
                        if (loTail != null) {
                            loTail.next = null;
                            newTab[j] = loHead;
                        }
                        if (hiTail != null) {
                            hiTail.next = null;
                            newTab[j + oldCap] = hiHead;
                        }










6f0a5bd8c4c67c49952e62feeb86dd5a.webp


















b09aa7f05c5b6f6f472e630ce16db3f1.webp







good-icon 0
favorite-icon 0
收藏
回复数量: 0
    暂无评论~~
    Ctrl+Enter