面试中如何回答HashMap的工作原理
内部原理是什么,比如存储模式,hashcode,扩展,默认容量等。
为什么JAVA8的ConcurrentHashMap放弃了分段锁?有什么问题?如果你来设计,你会怎么做?
设计。
有没有顺序的Map实现类,如果有,它们是如何保证顺序的?
hashmap的实现原理,/mbshqqq b/article/details/79799009
以下是我自己的总结:
存储结构:
其中存储了一个条目数组,每个数组元素中的结构是一个条目链表。
Entry是map中的一个静态内部类,其中的变量有:key、value、hashcocd和next Entry。
哈希是什么?
也称为哈希,通过哈希算法将任意长度的输入转换为固定长度的输出,输出是哈希值。这是一个压缩映射,哈希值的空间通常比输出的空间小很多。不同的输入可能列出相同的输出,因此无法从哈希值中确定唯一的输入值。这也是为什么比较两个对象不能只用hashcode方法进行比较的原因。
哈希冲突:
对不同值进行哈希运算会生成相同的哈希值。这是不可避免的,但我们需要尽量减少其损失。
(1)设计的哈希算法使其尽可能均匀分布。
hashmap中哈希算法的分析
静态最终int哈希(对象键){
int h;
return (key == null)?0:(h = key . hashcode())^(h & gt;& gt& gt16);
}
让密钥的hashcode右移16位,以混合其高低位,增加低位的随机性,同时变相保持高位的特性。
计算元素位置的算法:
int index = hash & amp(数组.长度-1);
那么就很清楚为什么HashMap的数组长度是2的整数次方了。例如,如果初始长度为16,则16-1 = 15,15的二进制数为000000000000000000000165438。可以看出,基数二进制的最后一位必须是1。当使用哈希值执行AND时,最后一位可能是0或1。但是,偶数和哈希值之间的AND运算的最后一位必须是0,这导致一些位置永远不会映射值。
空值的处理
Hashmap接受空值,这些值放在数组的第一个元素中。当我把它们拿出来的时候我应该做什么?
hashmap是如何工作的?
里面有一个入口数组。放的时候我们先根据键值计算hashcode,然后计算元素在数组中的位置,把键值对封装在Map中。条目对象,然后将其存储在数组中。
如果有哈希冲突怎么办?
每个数组元素的位置都是一个链表结构,如果计算出来的hashcode相同,就把元素追加到链表中,这就是hashmap的处理方法。
取元素时,先计算键值对应的hashcode找到元素的位置,然后调用key的equals方法遍历链表,最后找到元素。
还有哪些解决哈希冲突的方法?
开放式寻址方法
这种方法也叫再散列法。其基本思想是:当关键字key的hash地址p=H(key)冲突时,基于P生成另一个hash地址p1,如果p1仍然冲突,则基于P生成另一个hash地址p2,直到找到一个不冲突的hash地址pi,并在其中存储相应的元素。
再散列方法
这种方法是同时构造几个不同的散列函数:
Hi=RH1(key) i=1,2,…,k
当哈希地址Hi=RH1(key)冲突时,计算hi = rh2 (key)...直到冲突不再发生。这种方法不易产生聚合,但增加了计算时间。
链地址法
这种方法的基本思想是将所有哈希地址为I的元素组成一个名为同义词链的单链表,并将单链表的头指针存储在哈希表的第I个单元中,因此查找、插入和删除主要在同义词链中进行。链地址法适用于频繁插入和删除。
建立一个公共溢出区
该方法的基本思想是将哈希表分为基本表和溢出表两部分,所有与基本表冲突的元素都填充在溢出表中。
默认负载系数为0.75
当贴图的大小超过当前容量的75%时,它将自动扩展,原始对象将放回新数组中。
再散列的过程是什么样的?
说白了就是先调整大小生成一个新的Entry数组,然后转移重新计算原数组中的值放入新数组,再把阈值改成新的length x load factor。
如果键为空,它将始终被散列到表[0]的桶中,即使在重新散列的过程中也是如此。非空键也可能被哈希到table[0]的位置,比如上图中的key = "f ",同样的键也可能在不同的时间被哈希到不同的位置,这与重哈希有关。
这个过程中容易出现哪些问题?
容易出现条件竞争。如果在扩展的过程中放入数据,会造成意想不到的结果。或者如果两个线程都触发容量扩展,也会有问题。
有没有遇到过这一块的好例子?
jdk1.8之后改为红黑树。为什么?说说红黑树的原理?
hashmap和hashtable有什么区别?
HashTable和HashMap的实现原理几乎一样,区别无非就是
HashTable不允许空键和值。
哈希表是线程安全的。
但是HashTable线程安全的策略代价太大,简单粗暴,get/put的所有相关操作都是同步的,相当于给整个哈希表加了一把大锁。
多线程访问时,只要有一个线程访问或操作对象,其他线程就只能阻塞,相当于把所有操作都序列化了,在竞争的并发场景下性能会很差。
篇幅有限,未完待续。