热门标签 | HotTags
当前位置:  开发笔记 > 人工智能 > 正文

详解Java中的hashcode

这篇文章主要介绍了详解Java中的hashcode,文中有非常详细的代码示例,对正在学习java的小伙伴们有非常好的帮助,需要的朋友可以参考下

一、什么是hash

Hash,一般翻译做散列、杂凑,或音译为哈希,是把任意长度的输入(又叫做预映射pre-image)通过散列算法变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从散列值来确定唯一的输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。

这个说的有点官方,你就可以把它简单的理解为一个key,就像是map的key值一样,是不可重复的。

二、hash有什么用?,在什么地方用到?

1.在散列表

2.map集合

此处只是做了一个简单的介绍,其实远远没有那么简单,如算法里面的散列表,散列函数,以及map的一个不可重复到底是怎么样去判断的,以及hash冲突的问题,都与hash值离不开关系。

三、java中String类的hashcode方法

public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i 

可以看到,String类的hashcode方法是通过,char类型的方式进行一个相加,因为String类的底层就是通过char数组来实现的。

如:String str="ab"; 其实就是一个char数组 char[] str={'a','b'};

如:字符串 String star=“ab”;那么他对应的hash值就是:3105

怎么算出来的呢?

val[0]='a' ; val[1]='b'

a对应的Ascall码值为:97
b对的Ascall码值为:98
那么经过如下代码的循环相加

 for (int i = 0; i 

得出:97*31+98=3105;

我们再看: int h = hash;这一个代码,为什么有时候调用hashcode方法返回的hash值是负数呢?

因为如果这个字符串很长?那么h的值就会超过int类型的最大值,有没有想过,如果一个int类型的最大值,超过他的范围之后会怎么样呢?

我们先来看这样一个简单的代码:

 int count=0;
    while (true){
   if (count++<10){
            System.out.println("hello world");
          
        }
    }

大家认为hello world会输出多少次呢?
正常情况来说count小于10就不会输出了对么?
但是其实并不是这样的,很明确的告诉大家,这是一个死循环。

因为count一直加,最开始if成立,到后面的if不成立,再到后面if会再次成立,为什么会成立呢?因为count变为了负数。

???? 为什么?因为count一直无限制的加,由于是线性增长,计算速度是非常快的,所以,要不了多久,就会超出int类型的最大值。控制输出的count变为了负数,所以呢此时的if条件又成立了。

在这里插入图片描述

首先我们来看下int的范围 :int 为4个字节,一个字节为8位,一个int值是需要的存储空间是32位,但是呢?这是一个有符号位的int,需要一个符号位来表示正数和负数,
int值的范围就是:-2^31 ~ 2^31 也就是:-2147483648 到2147483647

我们来看下int最大值对应的二进制位是对少?

在这里插入图片描述

全是1? 2147483647最大值不是一个正数么?难道第一位难道不是应该用0表示么?

此时这个0他是省略掉了没写的,由于二进制系统是通过补码来保存数据的。第一位是符号位,0为正,1为负,当正的除了符号位全为1,再加1就进位了,符号位就会变成1,是负数,其他为0。
所以说当int的最大值加一个1,就变为了,最小值

来!口说无凭,没有说服力,我们直接来看代码:

在这里插入图片描述

那么我们在反过来想,最小值减1呢?那是不是就是对对应的是我们int类型的最大值了呀?

认真仔细的看完这个,你就知道为什么hashcode方法调用的时候有时候是正数,有时候是负数了吧?所以说底层还是很有意思的,比如往后做大数据开发,那么肯定是需要深入理解这些数据类型的范围,以及他的一个变化规则,否则有时候不知不觉的就犯错了,你还不知道错误在那儿?严重的话就是会损失精度,导致结果和你预期的结果相差甚远。就因为加了个1,就导致结果和预期结果相差十万八千里。

这是String类的hashcode方法的一个具体实现过程。

四、两个对象的 hashCode()相同,则 equals()也一定为 true,对吗?

首先呢?这个肯定是不对的。

因为这两个方法都可以重写,如果是自己的类,那么可以灵活重写,如果是jdk自己的类,那就要看他到底有没有重写这两个方法。

我们以String类的equlas方法为例:我们都知道,如果equlas方法如果没有重写,那就继承Object类的equlas方法,默认比较的是两个对象的内存地址,如果重写了,那就根据我们自己重写的规则来比较两个对象是否相等。

如下是jdkString类中自己重写的equals方法,

 public boolean equals(Object anObject) {
    // 如果两个String类型的对象内存地址相等直接返回true
        if (this == anObject) {
            return true;
        }
       // 如下做的操作就是比较两个字符串中的每一个char类型的数据,如果都完全匹配,那么就是返回true,如果有一个不相同,直接返回false,并且两个字符串的长度要相等,如果不相同,那么肯定也就不可能值一样了嘛
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

再看下String的hashcode方法:就是我们上面刚刚讲过的那个方法

 public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i 

1:如果两个String类型的对象内存地址相等直接返回true

2:比较两个字符串中的每一个char类型的数据,如果都完全匹配,那么就是返回true,如果有一个不相同,直接返回false,并且两个字符串的长度要相等,如果不相同,那么肯定也就不可能值一样了嘛

由此可以看出,equals方法是否返回true跟hashcode方法没有半毛钱关系嘛。

接下来我们看下,我们自定义的对象,我创建一个User类,里面有属性id和name

public class User {
    int id;
    String name;

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }


}

在没有重写hashcode方法和equals方法的情况下进行比较:

 User user1 = new User();
        User user2 = new User();

        System.out.println(user1.hashCode());
        System.out.println(user2.hashCode());
        System.out.println(user1.equals(user2));

在这里插入图片描述

解答:因为我们没有重写,这两个方法都是继承自Object类的,所以呢?比较的是内存地址,因为我们是通过new的方式去创建的两个user对象,那么他们的内存地址肯定是不相同的,所以直接返回false,而hashcode方法是java的底层语言c++来写的,具体他内部是怎么实现的,封装了,我也就不得而知了,后面再了解,有的人说是跟内存地址相关,但是具体我没有看到具体实现,也不敢苟同,有的东西,需要自己不断的去摸索,自己亲自实践,才是真的,不要相信别人说的。

好的,接下来我们重写User类的hashcode方法:

public class User {
    int id;
    String name;



    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }



    @Override
    public int hashCode() {
        return Objects.hash(id, name);
    }
}

在这里插入图片描述

可以看到的是,我们两个User对象的hashcode方法返回的hash值是完全一致的,但是通过equals方法进行比较,还是false,所以说,两个对象的hashcode方法返回的hash值相同,equlas也一定返回true是完全不成立的,直接推翻。

接下来我们再次重写equlas方法,此时我们规定,只要两个对象的id和name都相同,那么这两个对象就是同一个对象。并且删除掉刚刚我们重写的hashcode方法。

public class User {
    int id;
    String name;

    public User(int i, String name) {
        this.id = id;
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return id == user.id &&
                name.equals(user.name);
    }


}

在这里插入图片描述

通过运行结果,我们可以看到,hashcode方法返回的hash值不一致,但是我们的equlas方法依旧返回的是true,因为这个equlas方法是我们重写过了,在调用的时候,就不在去掉Object的equlas方法,进行比较内存地址,而是按照我们的重写规则:如果两个user对象的id和name相同,那么就是同一个对象,所以到这里,你是不是就具体的理解了hashcode方法个equlas之间的关系呢?

如果还是不明白,那再看一次,我们现在把User类的equlas方法和hashcode方法都写上,但是呢?我会创建两i不同的User对象(也就是id和name都不一样)。

public class User {
    int id;
    String name;

    public User(int i, String name) {
        this.id = id;
        this.name = name;
    }


    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return id == user.id &&
                name.equals(user.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name);
    }
}

在这里插入图片描述

由此可以看到,虽然hashcode方法返回的hash值相同,但是通过equlas方法进行比较,返回的直接就是false。

这里我们说的有点啰嗦了哈,本来三言两语就可以说清楚的,但是怕新手朋友不理解,所以就多啰嗦了几句,这个文章是我个人的理解,如果有不正确的希望你多参考书以及官网进行比对,毕竟眼见为实嘛,我也不敢完全保证我就是对的。

到此这篇关于详解Java中的hashcode的文章就介绍到这了,更多相关Java hashcode内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!


推荐阅读
  • 在解决ACM竞赛题目或力扣挑战时,通常面临1秒到2秒的时间限制。为了确保程序能够高效运行,C++等语言的代码执行次数建议不超过1千万次。 ... [详细]
  • 吴石访谈:腾讯安全科恩实验室如何引领物联网安全研究
    腾讯安全科恩实验室曾两次成功破解特斯拉自动驾驶系统,并远程控制汽车,展示了其在汽车安全领域的强大实力。近日,该实验室负责人吴石接受了InfoQ的专访,详细介绍了团队未来的重点方向——物联网安全。 ... [详细]
  • 本文介绍了多维缩放(MDS)技术,这是一种将高维数据映射到低维空间的方法,通过保持原始数据间的关系,以便于可视化和分析。文章详细描述了MDS的原理和实现过程,并提供了Python代码示例。 ... [详细]
  • 本文详细介绍了如何在Spring框架中设置事件发布器、定义事件监听器及响应事件的具体步骤。通过实现ApplicationEventPublisherAware接口来创建事件发布器,利用ApplicationEvent类定义自定义事件,并通过ApplicationListener接口来处理这些事件。 ... [详细]
  • TCP协议中的可靠传输机制分析
    本文深入探讨了TCP协议如何通过滑动窗口和超时重传来确保数据传输的可靠性,同时介绍了流量控制和拥塞控制的基本原理及其在实际网络通信中的应用。 ... [详细]
  • Maven + Spring + MyBatis + MySQL 环境搭建与实例解析
    本文详细介绍如何使用MySQL数据库进行环境搭建,包括创建数据库表并插入示例数据。随后,逐步指导如何配置Maven项目,整合Spring框架与MyBatis,实现高效的数据访问。 ... [详细]
  • 对于初学者而言,搭建一个高效稳定的 Python 开发环境是入门的关键一步。本文将详细介绍如何利用 Anaconda 和 Jupyter Notebook 来构建一个既易于管理又功能强大的开发环境。 ... [详细]
  • 使用TabActivity实现Android顶部选项卡功能
    本文介绍如何通过继承TabActivity来创建Android应用中的顶部选项卡。通过简单的步骤,您可以轻松地添加多个选项卡,并实现基本的界面切换功能。 ... [详细]
  • 软件测试行业深度解析:迈向高薪的必经之路
    本文深入探讨了软件测试行业的发展现状及未来趋势,旨在帮助有志于在该领域取得高薪的技术人员明确职业方向和发展路径。 ... [详细]
  • 本周三大青年学术分享会即将开启
    由雷锋网旗下的AI研习社主办,旨在促进AI领域的知识共享和技术交流。通过邀请来自学术界和工业界的专家进行在线分享,活动致力于搭建一个连接理论与实践的平台。 ... [详细]
  • 本题要求实现一个名为fun的函数,该函数的功能是从给定的字符串s中移除所有ASCII码为偶数值的字符,并将剩下的字符组成的新字符串存储在由t指向的数组中。 ... [详细]
  • 知识图谱与图神经网络在金融科技中的应用探讨
    本文详细介绍了融慧金科AI Lab负责人张凯博士在2020爱分析·中国人工智能高峰论坛上的演讲,探讨了知识图谱与图神经网络模型如何在金融科技领域发挥重要作用。 ... [详细]
  • 我的读书清单(持续更新)201705311.《一千零一夜》2006(四五年级)2.《中华上下五千年》2008(初一)3.《鲁滨孙漂流记》2008(初二)4.《钢铁是怎样炼成的》20 ... [详细]
  • MySQL InnoDB 存储引擎索引机制详解
    本文深入探讨了MySQL InnoDB存储引擎中的索引技术,包括索引的基本概念、数据结构与算法、B+树的特性及其在数据库中的应用,以及索引优化策略。 ... [详细]
  • AI炼金术:KNN分类器的构建与应用
    本文介绍了如何使用Python及其相关库(如NumPy、scikit-learn和matplotlib)构建KNN分类器模型。通过详细的数据准备、模型训练及新样本预测的过程,展示KNN算法的实际操作步骤。 ... [详细]
author-avatar
美竹
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有