在HashMap的底层实现中,采用了哈希表。 这是一个非常重要的数据结构。 对我们今后了解很多技术非常有帮助。 例如,redis数据库的核心技术与HashMap相同
数据结构通过数组和链表实现数据的存储,各有特点。
)1)排列:占用空间连续。 易于寻址,查询速度快。 但是,添加和删除效率非常低。
)2)链表)占用空间不连续。 寻址困难,查询速度慢。 但是,添加和删除非常有效率。
那么,是否可以将数组与链表的优点(即查询快,添加删除效率也高)结合起来? 答案是“哈希表”。 哈希表的本质是“数组链表”。
Hashmap基本结构的说明
哈希表的基本结构是“数组链表”。 打开HashMap源代码时,其中心内容如下:
其中的Node[] table是HashMap的核心数组结构,也称为“位桶数组”。 让我们看看什么是节点。 静态内部类Node
节点类存储:
1. key :关键对象
2 .值:值对象
3. next:以下节点
4 .混列:密钥对象的混列值
很明显,每个Node对象都是单向链表结构,以图形表示Node对象的典型图像。
HashMap的简单映像:
存储过程put (密钥,值)。
了解了HashMap的基本结构后,我继续深入学习HashMap如何存储数据。 其中的中心是生成用于与数组存储位置相对应的散列值的方法。
我们的目的是将“key-value个对象”存储在HashMap的Node[]数组中。 请参照以下步骤。
)1)获取密钥对象的hashcode
首先调用key对象的hashcode ()方法以获取hashcode。
)2)根据hashcode计算hash值([0,设为数组长度-1]的区间) ) ) )。
hashcode是整数,必须转换为[0,数组长度-1]的范围。 要求变换后的散列值尽量均等地分布在[0,数组长度-1]的区间,减少“散列冲突”
I .极端简单低的算法是:
散列值=hashcode/hashcode;
也就是说,混列值始终为1。 也就是说,键值和对象存储在数组索引1的位置,从而形成非常长的链表。 就像每次保存对象时都会发生“散列碰撞”一样,HashMap也退化为“链表”。
ii .一种简单而常用的算法是(相除剩馀算法) :
hash值=hashcode%数组长度
该算法可以将哈希值均匀分布在[0,数组长度-1]的区间内。 早期的HashTable采用了这种算法。 但是,这个算法使用了“除法”,所以效率很低。 JDK随后改进了算法。 首先,它保证数组的长度为2的整数幂。 由此,可以采用位运算来实现剩馀效果。 hash值=hashcode (数组长度-1)。
iii .采用JDK源代码的混列算法:
staticfinalinthash (对象密钥) {
int h;
返回(key==null )? 0:(h=key.hashcode () ) ) ) ) ) ) ) );
}
)3)生成节点对象
如上所述,一个Node对象包含四个部分: key对象、value对象、hash值和对以下Node对象的引用: 我们现在计算出了hash值。 对以下节点对象的引用为null :
)4)将Node对象放入table数组中
如果节点对象未位于与此节点对象相对应的数组索引位置,请将节点对象直接存储在数组中。 如果相应的索引位置已经有节点对象,则现有节点对象的next将指向此节点对象,从而形成链表。
总结以上流程。
添加元素" key-value "时,首先计算key的散列值以确定其在数组中的插入位置,但具有相同散列值的元素可能已经位于数组中的同一位置。 在这种情况下,它将添加到具有相同散列值的元素之后,并在数组的相同位置形成链表。 同一链表上的混列值相同,这意味着数组在JDK8中,如果链表长度超过8,链表将转换为红黑树,大大提高了搜索效率。
获取数据进程get(key )
必须从key对象中检索“键-值对”对象,然后返回到value对象。 了解存储数据的过程后,获取数据很容易。 请参阅以下步骤。
)1)获取key的hashcode,hash ) )通过散列算法获取hash值,并定位到数组位置。
)2)在链表中逐一比较key对象。 调用equals ()方法,将key对象与链表中所有节点的key对象进行比较,直到遇到返回true的节点对象。
)3) equals ) )返回true的节点对象的value对象。
知道了访问数据的过程。 让我们看看hashcode () )和equals方法之间的关系。
Java规定,两个内容相同且equals ()为true的对象必须具有相同的hashCode。 equals ) )为true,如果两个对象的hashcode不同,则在保存过程中出现了悖论。
扩张问题
混叠映射的位桶数组。 初始尺寸是16。 实际使用时,很明显大小是可变的。 位桶数组的元素达到(0.75*数组长度)后,将数组大小调整为两倍。
扩展需要时间。 扩展的本质是定义新的更大数组,然后将旧数组的内容一个接一个地复制到新数组中。
JDK8在链表大于8的情况下变为红黑二叉树
在JDK8中,HashMap存储单个元素时,如果相应的链表长度大于8,则链表将转换为红黑树,从而大大提高了搜索效率。