系统运维系列 之HashMap底层实现原理和应用

1 介绍

1HashMapJava中最常用的集合类框架,也是Java语言中非常典型的数据结构;
(2HashMap是基于哈希表的Map接口的非同步实现。元素以键值对的形式存放,并且允许null键和null值,因为key值唯一(不能重复),因此,null键只有一个;
(3HashMap不保证元素存储的顺序;
(4HashMap是线程不安全的。

2 HashMap的默认负载因子是0.75,而不是0.5或者是整数1呢?

临界值(threshold) = 负载因子(loadFactor) * 容量(capacity)
loadFactor是负载因子,表示HashMap满的程度,默认值为0.75f,也就是说默认情况下,当HashMap中元素个数达到了容量的3/4的时候就会进行自动扩容。

//code
void addEntry(int hash, K key, V value, int bucketIndex) {
	if ((size >= threshold) && (null != table[bucketIndex])) {
		resize(2 * table.length);
		hash = (null != key) ? hash(key) : 0;
		bucketIndex = indexFor(hash, table.length); 
	}
	createEntry(hash, key, value, bucketIndex);
}

HashMap其实是底层基于哈希函数实现的,但是哈希函数都有如下一个基本特性:根据同一哈希函数计算出的散列值如果不同,那么输入值肯定也不同。但是,根据同一散列函数计算出的散列值如果相同,输入值不一定相同。
两个不同的输入值,根据同一散列函数计算出的散列值相同的现象叫做碰撞。

为了避免哈希碰撞,HashMap需要在合适的时候进行扩容;默认的负载因子(0.75)在时间和空间成本之间提供了很好的权衡。更高的值减少了空间开销,但增加了查找成本。

//总结:1)负载因子不能太大,不然会导致大量的哈希冲突或者哈希碰撞;也不能太小,那样会浪费空间;
(2)阈值(threshold) = 负载因子(loadFactor) x 容量(capacity) 根据HashMap的扩容机制,他会保证容量(capacity)的值永远都是2的幂;为了保证负载因子x容量的结果是一个整数,这个值是0.75(4/3)比较合理,因为这个数和任何2的次幂乘积结果都是整数。

3 jdk8中HashMap为什么要引入红黑树?

1)解决jdk1.8以前hash冲突所导致的链化严重的问题,因为链表结构的查询效率是非常低的,他不像数组,能通过索引快速找到想要的值,链表只能挨个遍历,当hash冲突非常严重的时候,链表过长的情况下,就会严重影响查询性能;
(2)本身散列列表最理想的查询效率为O(1),当时链化后链化特别严重,他就会导致查询退化为O(n)为了解决这个问题所以jdk8中的HashMap添加了红黑树来解决这个问题,当链表长度>=8的时候链表就会变成红黑树。

4 HashMap和Hashtable计算hash的方法不同?

1HashMap计算hash:对key的hashcode进行了二次hash,以获得更好的散列值,然后对table数组长度取模。
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
static int hash(int h) {
        // 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);
 }
 static int indexFor(int h, int length) {
        return h & (length-1);
 }2Hashtable计算hash:直接使用key的hashcode对table数组的长度直接进行取模。
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;

5 HashMap 多线程操作导致死循环问题?

1)当重新调整HashMap大小的时候,确实存在条件竞争,因为如果两个线程都发现HashMap需要重新调整大小了,它们会同时试着调整大小;
(2)在调整大小的过程中,存储在链表中的元素的次序会反过来,因为移动到新的bucket位置的时候,HashMap并不会将元素放在链表的尾部,而是放在头部,这是为了避免尾部遍历(tail traversing)。如果条件竞争发生了,那么就死循环了。

HashMap可以通过下面的语句进行同步:
Map m = Collections.synchronizeMap(hashMap);

Hashtablesynchronized的,但是ConcurrentHashMap同步性能更好,因为它仅仅根据同步级别对map的一部分进行上锁;
ConcurrentHashMap当然可以代替HashTable,但是HashTable提供更强的线程安全性。

6 HashMap如何保存两个key相同的数据?

IdentityhashMap类利用哈希表实现Map接口,比较键(和值)时使用引用相等性代替对象相等性,也就是说key(value)比较的时候只比较两个key是否引用同一个对象,比较的是对象的地址。

一般Key的选择应为String等类型的数据,尽量不要使用引用数据类型。

参考资料:
https://blog.csdn.net/fengxi_tan/article/details/106629280 HashMap底层实现原理
https://blog.csdn.net/qq_29909965/article/details/78358066 hashmap出现重复key的情况
https://blog.csdn.net/qq_33732195/article/details/108121285?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1-108121285-blog-78358066.pc_relevant_default&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1-108121285-blog-78358066.pc_relevant_default&utm_relevant_index=2 HashMap如何保存两个key相同的数据

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值