[Java复习] 面试突击 - 集合

JDK 1.8 中hash算法和寻找算法是如何优化?

// JDK 1.8以后的HashMap里面的一段源码

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

高低16位参与运算:

比如有一个key的hash值

原值:        1111 1111 1111 1111     1111 1010 0111 1100

右移16位:0000 0000 0000 0000  1111 1111 1111 1111

异或运算: 1111 1111 1111 1111     0000 0101 1000 0011 -> 转换成int值表示hash值

寻址算法优化:

(n - 1) & hash  -> 算出数组里的一个位置下标

取模运算性能差一些,为了优化数组寻址过程,数组长度2的n次方,hash & (n – 1)效果跟hash对n取模效果一样,与运算性能更高。

核心在于低16的与运算

hash算法的优化:对每个hash值,在他的低16位中,让高低16位进行了异或,让他的低16位同时保持了高低16位的特征,尽量避免一些hash值后续出现冲突,大家可能会进入数组的同一个位置。

HashMap如何解决hash碰撞问题?

hash冲突问题,链表+红黑树,O(n)和O(logn)

map.put和map.get -> hash算法优化(避免hash冲突),寻址性能优化

假设你的链表很长,可能会导致遍历链表,性能会比较差,O(n)

优化,如果链表的长度达到了一定的长度之后,其实会把链表转换为红黑树,遍历一颗红黑树找一个元素,此时O(logn),性能会比链表高一些。

说说HashMap是如何进行扩容的?

2的N次方扩容。16,32,64。。。

rehash:

如果数组的长度扩容之后 = 32,重新对每个hash值进行寻址,也就是用每个hash值跟新数组的length - 1进行与操作。

n-1          0000 0000 0000 0000  0000 0000 0001 1111

hash1      1111 1111 1111 1111     0000 1111 0000 0101

&结果      0000 0000 0000 0000  0000 0000 0000 0101 = 5(index = 5的位置)

n-1          0000 0000 0000 0000 0000 0000 0001 1111

hash2     1111 1111 1111 1111     0000 1111 0001 0101

&结果    0000 0000 0000 0000   0000 0000 0001 0101 = 21(index = 21的位置)

判断二进制结果中是否多出一个bit的1,如果没多,那么就是原来的index,如果多了出来,那么就是index + oldCap,通过这个方式,就避免了rehash的时候,用每个hash对新数组.length取模,取模性能不高,位运算的性能比较高。

参考资料:

互联网Java工程师面试突击(第三季)-- 中华石杉

猜你喜欢

转载自www.cnblogs.com/fyql/p/12132477.html