算法6-1:哈希函数

算法6-1:哈希函数

大家好,又见面了,我是全栈君。

在上章节中已经介绍了通过红黑树实现键值对数组的查询操作,复杂度是logN。

有没有性能更好的算法呢?答案是有。

基本想法就是计算keyword的哈希值,再通过哈希值直接获取相应的键值。

这样的方法的须要解决的问题是:

  • 怎样计算哈希值

  • 怎样解决哈系冲突

哈希函数

目标

依据对象中的成员变量的值,依照一定的规则计算出一个整数。这个整数就是哈希值。

哈希值最重要的两个属性是:

  • 假设a.equals(b),那么a.hashCode() == b.hashCode()

  • 理想状况下。假设!a.equals(b),那么a.hashCode() != b.hashCode()

Java中的hash

Java中的Object对象中已经包括了hashCode函数,因为全部的对象都继承自Object,因此全部的对象都有hashCode函数。该函数能返回一个整数。代表这个实例的哈希值。

Java中Integer类型的hashCode代码例如以下:

public int hashCode() {
    return value;
}

Double类型的hashCode代码例如以下:

public int hashCode() {
    long bits = doubleToLongBits(value);
    return (int)(bits ^ (bits >>> 32));
}

String类型的hashCode代码例如以下:

public int hashCode() {
    int off = offset;
    char val[] = value;
    int len = count;
    int h = 0;
    for(int i = 0; i < len; i++) {
        h = 31*h + val[off++];
    }
    return h;
}

这样的计算哈系的办法称之为Hornor哈希法。这样的方法是一种很简单的哈系算法。构造哈系冲突是很easy的。在2011年11月,有人发现Java的HashMap存在漏洞easy让黑客实现Dos攻击,它的原理就是构造大量的哈系冲突让HashMap的复杂度从1变为N,占用大量的CPU资源,BUG的具体信息戳这里:https://bugzilla.redhat.com/show_bug.cgi?

id=CVE-2012-2739

因为String是不可变的类型,因此能够对hashCode进行缓存。

自己定义类型的hash计算

public class Student {    private int number;    private String name;    private String classname;     public int hashCode() {        int hash = 17;        hash = hash*31 + name.hashCode();        classname = hash*31 + classname.hashCode();    }}

其原理就是依照Hornor哈系法将各个成员变量的哈希值连接在一起。

哈希的取模操作

取模操作就是希望让哈系值能在0 ~ M-1范围内,便于通过它来訪问数组。

第一种方法的代码例如以下:

private int hash(Key key) {    return key.hashCode() % M;}

这段代码是错的。

这样的方法使用了取余数的操作,对于负数就会产生错误。

另外一种方法的代码例如以下:

private int hash(Key key) {    return Math.abs(key.hashCode()) % M;}

这段代码中有BUG。这样的方法在hashCode为负的0x80000000时会错误发生。由于它不能取相反数。

第三种方法的代码例如以下:

private int hash(Key key) {    return (key.hashCode() & 0x7fffffff) % M;}

这样的方法才是正确的。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/116054.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • ghost备份还原系统步骤_win10如何备份完整系统

    ghost备份还原系统步骤_win10如何备份完整系统Ghost在XP时代可以说是装机必备,因为Ghost使用简单、快捷,直到现在仍然受到强力的追捧。说到备份和还原操作系统,Ghost绝对是一把好手,简单的操作、快速的恢复,让你的电脑重新焕发活力。工具/原料:带有PE的U盘方法/步骤:用启动盘启动电脑,使它进入PE系统,双击桌面上的Ghost备份还原图标。备份系统1.单击Local—->Partition—->ToImage2.选择系统所在的硬盘(这里显示的是硬件的硬盘列表)…

    2025年9月19日
    5
  • Linux tomcat安装详解

    Linux tomcat安装详解欢迎访问我的个人博客网站:http://www.yanmin99.com/一、tomcat安装1、下载JDK和Tomcat//通过wget下载wgethttp://mirrors.tuna.tsinghua.edu.cn/apache/tomcat/tomcat-8/v8.5.4/bin/apache-tomcat-8.5.4.tar.gzwgethttp://download.ora

    2022年6月2日
    32
  • fsync操作

    fsync操作/*update需要刷磁盘的操作*/#0os_file_fsync_posix(file=20)at/data/mysql-boost-5.7.32/mysql-5.7.32/storage/innobase/os/os0file.cc:3081#10x000000000198c562inos_file_flush_func(file=20)at/data/mysql-boost-5.7.32/mysql-5.7.32/storage/innobase/os/os0file.c

    2022年5月31日
    47
  • Java递归调用_递归算法1加到100

    Java递归调用_递归算法1加到100递归用于解决什么样的问题?1)各种数学问题如:8皇后问题,汉诺塔,阶乘问题,迷宫问题,球和篮子的问题(google编程大赛)2)各种算法中也会使用到递归,比如快排,归并排序,二分查找,分治算法等.3)将用栈解决的问题–>递归代码比较简洁简单的说:递归就是方法自己调用自己,每次调用时传入不同的变量.递归有助于编程者解决复杂的问题,同时可以让代码变得简洁。递归需要遵守的重要规则:1…

    2022年9月2日
    5
  • python的几个有趣小程序「建议收藏」

    python的几个有趣小程序「建议收藏」最近整理一些python的小程序以及几个第三方库的简单使用,一方面用来熟悉手感,另一方面也用来休闲娱乐。文本进度条的编写:importtimescale=50print(“starting”.center(scale//2,”-“))start=time.perf_counter()foriinrange(scale+1): a=’*’*i b=’.’*(scale-i)…

    2022年6月22日
    144
  • pycharm英文读音_pycharm英文界面翻译

    pycharm英文读音_pycharm英文界面翻译使用的是PyCharm2018.3.4代码的自动补全在PyCharm中找到PowerSaveMode选项,将前面的对勾去掉。在左上角File的展开栏的倒数第二行在PyCharm的最右下角有个????的样子(在????旁边),单击点开就可看到PowerSaveMode选项在这个Currentinspectionprofile中可以设置HighlightingLevel即检查代码严格程度。(过多的不…

    2022年8月27日
    6

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号