Hash详解

Hash(哈希)

Hash散列,通过关于键值(key)的函数,将数据映射到内存存储中一个位置来访问。这个过程叫做Hash,这个映射函数称做散列函数,存放记录的数组称做散列表(Hash Table),又叫哈希表。JAVA函数hashCode()即请求对象的哈希值。

 

Hash的优点

先分类再查找,通过计算缩小范围,加快查找速度。

例:

集合:{13,19,25,27,17}

若是采用数组或链表结构:

访问其中的一个元素(如18),需要遍历整个集合的元素,时间复杂度为O(n).

而采用哈希表时:

假如散列函数为H[key] = key % 5;则集合元素对应的hash值分别为{3,4,0,2,2}。
访问元素(18)只需要在Hash值为2的集合中寻找即可.
如果访问没有哈希冲突的元素,例如访问(25),可以直接访问哈希值为(0)的值。
故hash时间复杂度最差才为O(n),最优情况下只需要O(1);
​

由上面的例子,我们可以想象,如果由大量的数据,采用数组或是链表存储时,访问需要遍历,耗费的时间非常多,而Hash表通过哈希计算,可以直接定位到数据所在位置(发生哈希冲突时,哈希值相同,可以定位到较小范围)大大加快了 查找的速度,节省了大量时间。

 

 

散列函数(Hash函数)

Hash通过Hash函数,将Key值映射为地址,Address = F[key];

常见的几种Hash函数:直接定址法、数字分析法、平方取中法、折叠法、随机数法、除留余数法

  1. 直接定址法:取Key或者Key的某个线性函数值为散列地址。Hash(k) = k,或者Hash(k) = a*k + b, (a\b均为常数).

    如下例所示:a = 1/100,b = -5.

    KeyHash(Key)
    100520010047
    300980030093
    150640015059
    760430076038

     

  2. 数字分析法:需要知道Key的集合,并且Key的位数比地址位数多,选择Key数字分布均匀的位。如下:

    Hash(Key) 取六位:

    列数 : 1   (2)   3   (4)   5   (6)   (7)   8   (9)   10   11   12   (13)


    key1:  5    2    4    2    7    5     8     5     3     6     5     1      3


    key2:  5    4    4    8    7    7     7     5     4     8     9     5      1


    key3:  3    1    5    3    7    8    5      4     6     3      5     5     2


    key4:  5    3    6    4    3    2      5    4     5      3      2    6     4

     

    其中(2、4、6、7、9、13) 这6列数字无重复,分布较均匀,取此六列作为Hash(Key)的值。

    Hash(Key1) :225833

    Hash(Key2):487741

    Hash(Key3):138562

    Hash(Key4):342554

     

  3. 平方取中法:取Key平方值的中间几位作为Hash地址。因为在设置散列函数时不一定知道所有关键字,选取哪几位不确定。一个数的平方的中间几位和数本身的每一位都有关,这样可以使随机分布的Key,得到的散列地址也是随机分布的 。如下:

    KeyKey值平方Hash地址(5位)
    1111121131234590165532476901655
    0101111010010223436343220134363
    2102221344419334562351395645623
  4. 折叠法:将关键字分割成位数相同的几部分(最后一部分的位数可以不同),然后取这几部分的叠加和(舍去进位)作为哈希地址。 当Key的位数较多的时候数字分布均匀适合采用这种方案.

    具体的叠加方法有两种,移位法和折叠法:

    例子:若Key为下列数串,地址位数为7,两种方法的Hash(key)分别如下:

    Key:7982374 | 1861215 | 9892154 | 56923

     移位折叠法: 折叠折叠法: 
    第一段7982374d1-dr7982374d1-dr
    第二段1861215d(r+1)-d(2r)5121681d(2r)-d(r+1)
    第三段9892154d(2r+1)-d(3r)4512989d(3r)-d(2r+1)
    第四段+ 56923d(3r+1)-d(end)+ 32965d(end)-d(3r+1)
    结果:19792666 17650009 
    Hash(key):9792666 7650009 
  5. 随机数法:伪随机探测再散列

    具体实现:建立一个伪随机数发生器,Hash(Key) = random(Key). 以此伪随机数作为哈希地址。

  6. 除留余数法:取关键字被某个除数 p 求余,得到的作为散列地址。

    即 H(Key) = Key % p;

    例子如下:

    KeyPHash(Key) (4位)
    1107630210006302
    1363527910005279
    4107655310006553
    7655302710003027

以上就是6种构造哈希函数的方法,选择时要本着尽量减少产生冲突的原则,根据Key值的位数,分布情况,范围大小做出更优的选择。

 

哈希冲突

不管选用何种散列函数,不可避免的都会产生不同Key值对应同一个Hash地址的情况,这种情况叫做哈希冲突。

 

哈希冲突的处理

当冲突发生时,我们需要想办法解决冲突,一般常用的方法有:开放定址法、单独链表法、双散列法、再散列法以及建业公共溢出取等方法。

  1. 开放定址法:

    当冲突发生时,探测其他位置是否有空地址 (按一定的增量逐个的寻找空的地址),将数据存入。根据探测时使用的增量的取法,分为:线性探测、平方探测、伪随机探测等。

    新的Hash地址函数为 Hash_new (Key) = (Hash(Key) + d i) mod m;i = 1,2...k (k<= m-1).m表示集合的元素数,i表示已经探测的次数。

    • 线性探测(Linear Probing)

      d i = a * i + b; a\b为常数。

      相当于逐个探测地址列表,直到找到一个空置的,将数据放入。

    • 平方探测(Quadratic Probing)

      d i = a * i 2 (i <= m/2) m是Key集合的总数。a是常数。

      探测间隔 i2 个单元的位置是否为空,如果为空,将地址存放进去。

    • 伪随机探测

      d i = random(Key);

      探测间隔为一个伪随机数。

       

       

    例子:

    Key集合为(15,36,25,46,75),采用除留余数法,模10 ,冲突时采用线性探测法d i = i;

    如图,15和36放入时无冲突发生,分别对应地址 5 和6 。

    但是当25放入时,Hash(25) = 25%10 = 5,和Key= 15发生冲突,则令d 1 = 1,Hash(25) = (25 + 1) %10 = 6.

    此时已然与Key = 36发生冲突,令d 2 = 2,Hash(25) = (25+2) %10 = 7,无冲突,放入。

    此后46,75也发生冲突,解决方法同上。

 

  1. 链表法

    将散列到同一个位置的所有元素依次存储在单链表中,或者也有存储在栈中。具体实现根据实际情况决定这些元素的数据存储结构。

    如下图所示:

    首页Hash地址为 i 的结点,均插入到以 T[i] 为头指针的单链表中。T 中各分量的初值均应为空指针。

    在拉链法中,装填因子 α 可以大于 1,但一般均取 α ≤ 1。

     

    装填因子(载荷因子)

    散列表的载荷因子定义为:

    ​ α = 填入表中的元素个数 / 散列表的长度.

    α 是散列表装满程度的标志因子。由于表长是定值,α 与“填入表中的元素个数”成正比,所以,α 越大,表明填入表中的元素越多,产生冲突的可能性就越大;反之,α 越小,标明填入表中的元素越少,产生冲突的可能性就越小。实际上,散列表的平均查找长度是载荷因子α 的函数,只是不同处理冲突的方法有不同的函数。

    对于开放定址法,荷载因子是特别重要因素,应严格限制在0.7-0.8以下。超过0.8,查表时的CPU缓存不命中(cache missing)按照指数曲线上升。因此,一些采用开放定址法的hash库,如Java的系统库限制了荷载因子为0.75,超过此值将resize散列表。

  • 58
    点赞
  • 332
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
### 回答1: MySQL 中的 hash join 是一种连接两个表的方法,它通过将其中一个表的数据哈希到内存中的哈希表来实现。另一个表的数据在哈希表上进行匹配,从而得到符合条件的行。 这种方法适用于其中一个表的数据量较小的情况。因为哈希表的查询速度很快,所以这种方法效率高。但是,如果其中一个表的数据量很大,则需要将整个表加载到内存中,这可能会导致内存不足。 在 MySQL 中,使用 INNER JOIN、LEFT JOIN 或 RIGHT JOIN 关键字可以实现 hash join 操作。 ### 回答2: MySQL的hashJoin是一种关联查询的算法,用于将两个表中的数据进行关联。它通过计算每个表的关联列的哈希值,将相同哈希值的记录分配到同一个哈希桶中,然后对每个哈希桶进行内存中的关联操作。 首先,hashJoin需要将两个要关联的表按照关联列进行哈希分区。然后,对于第一个表的每个分区,将其哈希列值与第二个表进行匹配。如果哈希值相同,则将两个记录进行关联,生成结果。 hashJoin的优点是在内存充足的情况下,处理大规模数据的效率较高。由于哈希表是在内存中构建的,所以可以减少磁盘I/O的开销。此外,它适用于多种关联类型,如内连接、左连接、右连接等。 然而,hashJoin也有一些限制。首先,它需要将整个表进行哈希分区,因此在内存不足的情况下,可能导致性能下降。其次,在进行哈希分区和关联操作时,需要消耗较多的CPU资源。此外,如果两个表中的关联列不具有相同的数据分布,可能导致哈希桶不均匀,进而影响关联操作的效率。 总的来说,MySQL的hashJoin算法是一种高效的关联查询方法,可以在合适的场景下提供较好的性能。但需要注意配置合适的内存大小,并保证关联列的数据分布较为均匀,以达到最佳的运行效果。 ### 回答3: MySQL中的hashJoin是一种用于联接操作的算法。联接操作是将多个表中的数据按照某些条件进行匹配和合并的过程。而hashJoin是其中一种高效的联接算法。 hashJoin的原理是利用哈希表的特性,在内存中构建一个哈希表来存储较小表中的数据。首先,将待联接的两个表中的一个表的数据读入内存并构建哈希表,将哈希表的键值设为联接条件的键值,并将相应的数据行存储在哈希表中。然后,遍历另一个表的数据,对于每一行数据,通过联接条件的键值查找哈希表中是否存在对应的数据行,如果存在,则将两行数据进行合并,并输出结果。 相比于其他联接算法,hashJoin的优点主要体现在以下几个方面: 1. 内存控制:hashJoin将较小表的数据存储在内存中,避免了大规模的磁盘读写操作,提高了查询效率。同时,由于使用哈希表存储数据,可以大幅度减少内存的占用空间。 2. 快速查找:哈希表的查找操作具有快速的特性,可以在常数时间内完成查找操作。这使得hashJoin能够在较短的时间内完成联接操作,适用于处理大规模数据的场景。 3. 并行化处理:由于hashJoin的哈希表是在内存中构建的,可以方便地进行并行化处理。可以将不同的数据分配给不同的CPU进行处理,提高了联接操作的并行度和效率。 需要注意的是,hashJoin算法对内存的需求较高,如果内存不足,可能会导致性能下降或者无法完成联接操作。此外,选择合适的联接条件和恰当的索引也会对hashJoin的效率产生影响。因此,在使用hashJoin时,需要根据具体的业务场景和数据特点进行调优和优化。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值