算法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)
上一篇 2022年1月26日 下午7:00
下一篇 2022年1月26日 下午7:00


相关推荐

  • “SqlTransaction 已完成;它再也无法使用”解决方法

    “SqlTransaction 已完成;它再也无法使用”解决方法 当只是使用一次事务时,只用简单的事务就可以了示例代码:      SqlServerDataBaseobj=newSqlServerDataBase();       SqlConnectionconn=obj.DBconn();       conn.Open();       SqlTransactionmyTrans;       myTrans=co

    2022年5月20日
    36
  • vs生成sln文件_VS二进制文件

    vs生成sln文件_VS二进制文件VisualStudio.NET采用两种文件类型(.sln和.suo)来存储特定于解决方案的设置,它们总称为解决方案文件。为解决方案资源管理器提供显示管理文件的图形接口所需的信息,从而在每次继续开发任务时,不会因开发环境而分散精力;*.sln:(VisualStudio.Solution)通过为环境提供对项目、项目项和解决方案项在磁盘上位置的引用,可将它们组织到解决方案…

    2022年5月3日
    593
  • 激活码2021。3【在线注册码/序列号/破解码】

    激活码2021。3【在线注册码/序列号/破解码】,https://javaforall.net/100143.html。详细ieda激活码不妨到全栈程序员必看教程网一起来了解一下吧!

    2022年3月17日
    92
  • 简单模拟mybatis的MapperScan

    简单模拟mybatis的MapperScan一、问题描述在mybatis中,mapper通常是一个接口,但是我们却可以直接通过这个接口调用方法。按道理来说接口是不能直接调用方法的,只有实现类才能调用接口。但在下面的代码中,我们直接调用applicationContext.getBean(TestMapper.class).list(“”),就可以查询我们的数据库。也就是说applicationContext.getBean(TestMa…

    2022年5月6日
    73
  • 此工作站和主域间的信任关系失败 又一解决办法_电脑加域后无管理员

    此工作站和主域间的信任关系失败 又一解决办法_电脑加域后无管理员某虚拟化的域控制器出现严重故障以至于不可修复,故使用之前Hyper-V中导出的备份恢复了域控制器。恢复后基本功能正常,但部分工作站登录时提示“此工作站和主域间的信任关系失败”。【解决方案】0、必须确保故障工作站没有其他的问题(如网络连接故障、DNS设置错误等);1、在不能登录域的工作站上,使用工作站本地的管理员用户登录系统;2、在工作站上打开powershell,输入Reset…

    2022年10月19日
    5
  • Markdown进阶(更改字体、颜色、大小,设置文字背景色,调整图片大小设置居中)

    Markdown进阶(更改字体、颜色、大小,设置文字背景色,调整图片大小设置居中)基础知识 Markdown 通过简单标记语法 使普通文本内容具有一定格式 但它本身不支持修改字体 字号与颜色等功能的 CSDN markdown 编辑器是其衍生版本 支持基于 PageDown StackOverflo 所使用的编辑器的扩展功能 如表格 脚注 内嵌 HTML 内嵌 LaTeX 等等 Size 规定文本的尺寸大小 取值从 1 到 7 浏览器默认值是 3

    2026年3月18日
    3

发表回复

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

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