zoukankan      html  css  js  c++  java
  • 浅谈HashMap 的底层原理

    本文整理自漫画:什么是HashMap? -小灰的文章 。已获得作者授权。


    HashMap 是一个用于存储Key-Value 键值对的集合,每一个键值对也叫做Entry。这些个Entry 分散存储在一个数组当中,这个数组就是HashMap 的主干。
    HashMap 数组每一个元素的初始值都是Null
    这里写图片描述

    1. Put 方法的原理

    调用Put方法的时候发生了什么呢?
    比如调用 hashMap.put("apple", 0) ,插入一个Key为“apple"的元素。这时候我们需要利用一个哈希函数来确定Entry的插入位置(index):
    index = Hash("apple")
    假定最后计算出的index是2,那么结果如下:
    这里写图片描述
    但是,因为HashMap的长度是有限的,当插入的Entry越来越多时,再完美的Hash函数也难免会出现index冲突的情况。比如下面这样:
    这里写图片描述
    这时候该怎么办呢?我们可以利用链表来解决。
    HashMap数组的每一个元素不止是一个Entry对象,也是一个链表的头节点。每一个Entry对象通过Next指针指向它的下一个Entry节点。当新来的Entry映射到冲突的数组位置时,只需要插入到对应的链表即可:
    这里写图片描述
    新来的Entry节点插入链表时,使用的是“头插法。

    2. Get方法的原理

    使用Get方法根据Key来查找Value的时候,发生了什么呢?
    首先会把输入的Key做一次Hash映射,得到对应的index:
    index = Hash(“apple”)
    由于刚才所说的Hash冲突,同一个位置有可能匹配到多个Entry,这时候就需要顺着对应链表的头节点,一个一个向下来查找。假设我们要查找的Key是“apple”:
    这里写图片描述

    第一步,我们查看的是头节点Entry6,Entry6的Key是banana,显然不是我们要找的结果。
    第二步,我们查看的是Next节点Entry1,Entry1的Key是apple,正是我们要找的结果。
    之所以把Entry6放在头节点,是因为HashMap的发明者认为,后插入的Entry被查找的可能性更大。

    3. HashMap的初始长度

    初始长度为16,且每次自动扩容或者手动初始化的时候必须是2的幂。
    如何进行位运算呢?有如下的公式(Length是HashMap的长度):
    之前说过,从Key映射到HashMap数组的对应位置,会用到一个Hash函数:
    index = Hash(“apple”)
    如何实现一个尽量均匀分布的Hash函数呢?我们通过利用Key的HashCode值来做某种运算。
    index = HashCode(Key) & (Length - 1)
    下面我们以值为“book”的Key来演示整个过程:

    1. 计算book的hashcode,结果为十进制的3029737,二进制的101110001110101110 1001
    2. 假定HashMap长度是默认的16,计算Length-1的结果为十进制的15,二进制的1111。
    3. 把以上两个结果做与运算,101110001110101110 1001 & 1111 = 1001,十进制是9,所以 index=9。
      可以说,Hash算法最终得到的index结果,完全取决于Key的Hashcode值的最后几位。这里的位运算其实是一种快速取模算法。

    HashMap 的size为什么必须是2的幂?。这是因为2的幂用二进制表示时所有位都为1,例如16-1=15 的二进制就是1111B。我们说了Hash算法是为了让hash 的分布变得均匀。其实我们可以把1111看成四个通道,表示跟1111 做&运算后分布是均匀的。假如默认长度取10,二进制表示为1010,这样就相当于有两个通道是关闭的,所以计算出来的索引重复的几率比较大。

    想看原作者的文章可以看看这个公众号。
    这里写图片描述

  • 相关阅读:
    Kubernetes 集成研发笔记
    Rust 1.44.0 发布
    Rust 1.43.0 发布
    PAT 甲级 1108 Finding Average (20分)
    PAT 甲级 1107 Social Clusters (30分)(并查集)
    PAT 甲级 1106 Lowest Price in Supply Chain (25分) (bfs)
    PAT 甲级 1105 Spiral Matrix (25分)(螺旋矩阵,简单模拟)
    PAT 甲级 1104 Sum of Number Segments (20分)(有坑,int *int 可能会溢出)
    java 多线程 26 : 线程池
    OpenCV_Python —— (4)形态学操作
  • 原文地址:https://www.cnblogs.com/rever/p/8108055.html
Copyright © 2011-2022 走看看