面试之HashMap

原理

HashMap基于Hash算法,我们通过put(key,value)存储,get(key)来获取。当传入key时,HashMap会根据key.hashCode()计算出hash值,根据hash值将value保存在bucket里。当数据多的时候肯定会有计算出来的hash值是相同的,这时候发生hash冲突。当数据过多的时候也会自动扩容。

hash冲突

jdk1.8之前:数组加链表,hash冲突它会将相同hash值的value追加到链表后面,然后去遍历它。

jdk1.8之后:数组加链表加红黑树,引入了一个阈值,那么当大于默认阈值8的时候,它就会将链表变成一个红黑树。

自动扩容

如果在初始化HashMap中没有指定初始容量,那么默认容量为16,负载因子是0.75,如果后来HashMap中存放的数量超过了16×0.75=12的时候,HashMap便会调用resize()方法,扩大为原来的2倍(2的n次幂)。

线程不安全

扩容引发的线程不安全:

jdk1.8之前,它会调用到transfer()方法,采用头插法的形式,头插法会将链表的顺序翻转,会形成死循环以及数据丢失的问题。

jdk1.8之后,就没有transfer()方法了,采用了尾插法的形式,在并发执行put操作时会发生数据覆盖的情况。

如何保证线程安全

首用CurrentHashMap,也可以用HashTable、加锁、Collections.synchronizedMap(),视情况而定。

为什么用 CurrentHashMap 而不用 HashTable

在迭代的过程中,HashTable加了一个对象锁,会锁定整个map,在多线程环境中效率很低,而ConcurrentHashMap使用分段锁,降低了锁粒度,让并发度大大提高。ConcurrentHashMap将hash表分为16个桶(默认值),诸如get,put,remove等常用操作只锁当前需要用到的桶。试想,原来只能一个线程进入,现在却能同时16个写线程进入并发性的提升是显而易见。

fail-fast

“快速失败”也就是fail-fast,它是Java集合的一种错误检测机制。当多个线程对集合进行结构上的改变的操作时,有可能会产生fail-fast机制。记住是有可能,而不是一定。例如:假设存在两个线程(线程1、线程2),线程1通过Iterator在遍历集合A中的元素,在某个时候线程2修改了集合A的结构(是结构上面的修改,而不是简单的修改集合元素的内容),那么这个时候程序就会抛出ConcurrentModificationException 异常,从而产生fail-fast机制。

你可能感兴趣的:(面试,java,hashmap)