热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

覆盖equals的时候总要覆盖hashCode

importjava.util.HashMap;publicclassStudent{privateStringname;privateSt
import java.util.HashMap;


public class Student {

    private String name ;
    
    private String id;
    
    public Student(String name , String id) {
        this.id = id;
        this.name = name;
    }
    
    public String getName() {
        return this.name;
    }
    
    public String getId() {
        return this.id;
    }
    
    @Override
    public boolean equals(Object o) {
        if(o==this)
            return true;
        if(!(o instanceof Student)) 
            return false;
        
        Student t = (Student)o;
        if(!name.equals(t.getName())) 
            return false;
        if(!id.equals(t.getId()))
            return false;
        return true;
    }
    
    public static void main(String[] args) {
        HashMap map = new HashMap();
        Student t1 = new Student("s1","1");
        Student t2 = new Student("s1","1");
        System.out.println(t1.equals(t2));
        map.put(t1, 1);
        map.put(t2, 1);
        System.out.println(map.size());
    }
    
}

运行结果
true
2

从上面运行 的结果就可以看到,虽然两个对象是“相等的”,但是map中却存放着两个值,这与map中key值唯一似乎的矛盾的,这个出现这个原因就是没有重写hashCode()方法,两个对象的散列码不一样,map在处理的时候就当做不同的对象来处理。---(因为hashcode不一样,那么认为equals方法也为false),如果如下所示,重写hashCode()方法,则只有一个值

@Override
    public int hashCode() {
        int result = 17;
        int c = 0;
        c = this.name.hashCode();
        result = 31 * result+c;
        c= this.id.hashCode();
        result = 31*result+c;
        return result;
    }

 

在Object规范中,有如下内容:

1) 只要对象的equals方法的比较操作所用到的信息没有被修改,那么对这同一个对象调用多次,hashCode方法都必须始终如一的返回同一个整数。在一个应用程序的多次执行过程中,每次执行所返回的整数可以不一致。 
2)如果连个对象根绝equals方法比较是相等的,那么调用这两个对象中任意一个对象的hashCode方法都必须产生同样的整数结果。 

3)如果两个对象根据equals方法比较是不相等的,那么调用这两个对象中任意一个对象的hashCode方法,则不一定产生不同的整数结果。

如果覆盖equals时没有覆盖hashCode方法,则违反了约定的第二条。

 

一个好的hashCode函数通常倾向于“为不相等的对象产生不相等的散列码”,这正是hashCode约定中第三条的意义。

一个比较好的生成hashCode函数的方法如下:

1、把某个非零常数值,比如说17,保存在一个叫result 的int 类型的变量值中。

2、对于对象中的每一个关键域f (指equals 方法中考虑的每一个域,非equals用到的域一概不要考虑),完成以下步骤:

  • a 为该域计算int 类型的散列码c:
    • i如果该域是boolean 类型,则计算(f ? 1 : 0);
    • ii如果该域是byte、char、short 或者int 类型,则计算(int)f;
    • iii如果该域是long 类型,则计算(int)(f ^ (f >>> 32));
    • iv如果该域是float 类型,则计算Float.floatToIntBits(f);
    • v如果该域是double 类型,则计算Double.doubleToLongBits(f) 得到一个long 类型的值,然后按照步骤2.a.iii 对该long 类型计算散列值;
    • vi如果该域是一个对象引用,并且该类的equals 方法通过递归调用equals 的方式来比较这个域,则同样对这个域递归调用hashCode 方法;如果要求一个更为复杂的比较,则为这个域计算一个“规范表示”,然后针对这个规范表示调用hashCode。如果这个域的值为null,则返回0;
    • vii如果该域是一个数组,则把每一个元素当做单独的域来处理。然后根据步骤2.b 中的做法把这些散列值组合起来。
  • b 按照下面的公式,把步骤a 中计算得到的散列码c 组合到result 中:
    result = 31 * result + c;

3、返回result 值。

注:根据实践经验,在对ASCII 串的散列函数中,31 和37 是很好的散列因子。

注意:必须排除equals方法没有用到的任何域。也不要试图从散列码中排除掉一个对象的关键部分来提高性能。

如果一个类是不可变的,并且计算散列值的代价也比较大,那么就没有必要每次都计算了,应该考虑把散列码缓存在对象内部。


推荐阅读
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文介绍了如何在给定的有序字符序列中插入新字符,并保持序列的有序性。通过示例代码演示了插入过程,以及插入后的字符序列。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • C语言注释工具及快捷键,删除C语言注释工具的实现思路
    本文介绍了C语言中注释的两种方式以及注释的作用,提供了删除C语言注释的工具实现思路,并分享了C语言中注释的快捷键操作方法。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文介绍了Java高并发程序设计中线程安全的概念与synchronized关键字的使用。通过一个计数器的例子,演示了多线程同时对变量进行累加操作时可能出现的问题。最终值会小于预期的原因是因为两个线程同时对变量进行写入时,其中一个线程的结果会覆盖另一个线程的结果。为了解决这个问题,可以使用synchronized关键字来保证线程安全。 ... [详细]
  • Android JSON基础,音视频开发进阶指南目录
    Array里面的对象数据是有序的,json字符串最外层是方括号的,方括号:[]解析jsonArray代码try{json字符串最外层是 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
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社区 版权所有