算法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)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 开源协议解读

    开源在今天的软件业已经很普遍,但开源是否意味着使用者可以对开源后的代码为所欲为呢?答案是否定的。开源运动同样有自己的游戏规则和道德准则。不遵行这些规则不但损害开源运动的健康发展,也会对违规者造成名誉和

    2021年12月22日
    63
  • JavaScript 检查是否是数字

    JavaScript 检查是否是数字

    2021年9月4日
    49
  • 笔记本卡顿不流畅是什么原因_笔记本卡顿不流畅是什么原因_笔记本电脑卡顿不流畅如何解决-win7之家…「建议收藏」

    笔记本卡顿不流畅是什么原因_笔记本卡顿不流畅是什么原因_笔记本电脑卡顿不流畅如何解决-win7之家…「建议收藏」有不少笔记本电脑用户在使用过程中,发现会经常会遇到卡顿不流畅的情况,很多用户不知道是什么原因引起的,其实原因有很多,可能是电脑本身配置不足,或者电脑占用率过高,或者内存不足等,接下来给大家带来笔记本电脑卡顿不流畅的详细解决方法吧。具体步骤如下:1、CPU不足电脑卡顿很多时候都是因为CPU占用过高,实质还是CPU太小引起的,我们可以将多余的进程或者软件关闭,或者更换性能好的CPU来解决这个问题,电脑…

    2025年10月30日
    6
  • @transactional的使用_@transactional注解默认的回滚方式

    @transactional的使用_@transactional注解默认的回滚方式@Transactional是声明式事务管理编程中使用的注解1.添加位置1)接口实现类或接口实现方法上,而不是接口类中。2)访问权限:public的方法才起作用。@Transactional注解应该只被应用到public方法上,这是由SpringAOP的本质决定的。系统设计:将标签放置在需要进行事务管理的方法上,而不是放在所有接口实现类上:只读的接口就不需要事务管…

    2022年9月30日
    3
  • String类型转int,转long

    String类型转int,转longStringstr1="123";Stringstr2="123.0";不带小数:可直接可转为intinta=Integer.parseInt(str);带小数,直接转为int会报数字格式化异常,需要先转为double,后转为int转int: intb=(int)Double.parseDouble(str);转long:longc =(lon…

    2022年6月5日
    36
  • 微信小程序面试题总结

    微信小程序面试题总结小程序面试题简单描述下微信小程序的相关文件类型?一、WXML(WeiXinMarkupLanguage)是框架设计的一套标签语言,结合基础组件、事件系统,可以构建出页面的结构。内部主要是微信自己定义的一套组件。与html差不多。二、WXSS(WeiXinStyleSheets)是一套样式语言,用于描述WXML的组件样式,与css差不多二、js逻辑处理,…

    2022年6月26日
    42

发表回复

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

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