HashMap基础原理(  ૢ⁼̴̤̆ ꇴ ⁼̴̤̆ ૢ)~ෆ

前言

HashMap是基于哈希表的Map接口实现,它提供所有可选的映射操作并允许使用null只和null键,但不保证映射的顺序。HashMap是基于HashCode的,若想正确使用它就需要重写hashCode()和equals()方法。HashMap不是线性安全的,若想要线性安全,可用:

Map m=Collections.synchronizedMap(new HashMap());

一、JDK1.7版本的HashMap

1.1、数据结构
HashMap整体是一个数组,数组上的每个位置是一个链表,链表的节点存储着一个Entry对象,Entry对象有4个属性,分别为hash、key、value、next。

HashMap的整体结构如下(查看原图):
在这里插入图片描述
简单来说HashMap的数据结构是数组+链表,数组是HashMap的主题,链表是为了解决哈希冲突而存在的。(下面会讲到什么是哈希冲突)

1.2、工作原理
首先初始化HashMap,提供了有参构造和无参构造,在无参构造中,initialCapacity(默认数组大小)为16,loadFactory(加载因子)默认为0.75,阈值默认为initialCapacity*loadFactory=16*0.75=12
我们HashMap的插入原理为例,
第一步:通过HashMap自己提供的hash函数算出 当前key的hash值。(以下是jdk1.7的hash函数代码)

static int hash(int h) {
    h ^= (h >>> 20) ^ (h >>> 12);
    return h ^ (h >>> 7) ^ (h >>> 4);
}
}

第二步:通过计算出的hash值调用indexFor()方法,计算出当前对象应该存储在数组的位置。
indexFor()方法源码:

static int indexFor(int h, int length) { // h 为key 的 hash值;length 是数组长度
        return h & (length-1);  
 }

第三步:判断容器中已有Entry数量是否已经达到阈值,如果没有则继续,如果达到阈值,那么就将数组容量扩充到原来的2倍。

第四步:将当前对应的hash、key、value封装为一个Entry对象,查找数组在当前的存储位置上有没有元素,如果没有则放在这个位置上,如果在此位置上已经存在链表(发生哈希冲突),那么就遍历链表,如果链表上某个节点的key与当前key进行equals()比较后结果为true,就返回原来key所对应的value值,并用当前key所对应的value将其替换掉。如果遍历完列表,没有找到key与当前key进行equals()比较为true的,就把刚才封装的Entry中的next指向当前链表的头结点,也就是当前节点在链表的第一个位置。
至此,我们已经将key-value存储到容器中。

需要注意的是,每次扩容之后,都要重新计算原来的 Entry 在新数组中的位置,由indexFor()源码得知,元素所在位置是和数组长度是有关系的,既然扩容后数组长度发生了变化,那么元素位置肯定是要发生变化了。
流程图为(查看原图):
在这里插入图片描述

二、JDK1.8中的HashMap

JDK1.8在JDK1.7的基础上针对增加了红黑树来进行优化。
数据结构图如下(查看原图):
在这里插入图片描述
插入原理流程图如下(查看原图):
在这里插入图片描述
jdk1.8的hash函数:

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

这么设计有二点原因:

1.一定要尽可能降低hash碰撞,越分散越好;
2.算法一定要尽可能高效,因为这是高频操作, 因此采用位运算;

总结:dk1.8的HashMap主要有三点优化:

1.数组+链表改成了数组+链表或红黑树;

2.链表的插入方式从头插法改成了尾插法,简单说就是插入时,如果数组位置上已经有元素,1.7将新元素放到数组中,原始节点作为新节点的后继节点,1.8遍历链表,将元素放置到链表的最后;

3.扩容的时候1.7需要对原数组中的元素进行重新hash定位在新数组的位置,1.8采用更简单的判断逻辑,位置不变或索引+旧容量大小;

4.在插入时,1.7先判断是否需要扩容,再插入,1.8先进行插入,插入完成再判断是否需要扩容;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值