1. import java.util.HashMap;
  2.  
  3. public class Student {
  4.  
  5. private String name ;
  6.  
  7. private String id;
  8.  
  9. public Student(String name , String id) {
  10. this.id = id;
  11. this.name = name;
  12. }
  13.  
  14. public String getName() {
  15. return this.name;
  16. }
  17.  
  18. public String getId() {
  19. return this.id;
  20. }
  21.  
  22. @Override
  23. public boolean equals(Object o) {
  24. if(o==this)
  25. return true;
  26. if(!(o instanceof Student))
  27. return false;
  28.  
  29. Student t = (Student)o;
  30. if(!name.equals(t.getName()))
  31. return false;
  32. if(!id.equals(t.getId()))
  33. return false;
  34. return true;
  35. }
  36.  
  37. public static void main(String[] args) {
  38. HashMap<Student , Integer> map = new HashMap<Student, Integer>();
  39. Student t1 = new Student("s1","");
  40. Student t2 = new Student("s1","");
  41. System.out.println(t1.equals(t2));
  42. map.put(t1, );
  43. map.put(t2, );
  44. System.out.println(map.size());
  45. }
  46.  
  47. }
  48.  
  49. 运行结果
    true
    2

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

  1. @Override
  2. public int hashCode() {
  3. int result = ;
  4. int c = ;
  5. c = this.name.hashCode();
  6. result = * result+c;
  7. c= this.id.hashCode();
  8. result = *result+c;
  9. return result;
  10. }

在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方法没有用到的任何域。也不要试图从散列码中排除掉一个对象的关键部分来提高性能。

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

覆盖equals的时候总要覆盖hashCode的更多相关文章

  1. 第九条:覆盖equals方法时总要覆盖hashCode方法

    Object类的hashCode方法: public native int hashCode();   是一个本地方法. 其中这个方法的主要注释如下: Whenever it is invoked o ...

  2. 【Java实战】源码解析为什么覆盖equals方法时总要覆盖hashCode方法

    1.背景知识 本文代码基于jdk1.8分析,<Java编程思想>中有如下描述: 另外再看下Object.java对hashCode()方法的说明: /** * Returns a hash ...

  3. Item 9 覆盖equals时总要覆盖hashCode

    为什么覆盖equals时,总要覆盖hashCode?   原因是,根据Object规范: 如果两个对象根据equals(Object)方法比较是相等的,那么调用这两个对象中任意一个对象的hashCod ...

  4. 第八条:覆盖equals时请遵守通用约定

    ==是物理相等 equals是逻辑相等 因为每个类的实例对象本质上都是唯一的 ,利用物理相等(==)是指一个实例只能相等于它自己. 利用逻辑相等是(equals)指 一个实例是否和另一个实例的某些关键 ...

  5. 第八条——覆盖equals方法时需遵守的通用约定

    1)自反性 对于任何非null的引用值x,x.equals(x)必须返回true.---这一点基本上不会有啥问题 2)对称性 对于任何非null的引用值x和y,当且仅当x.equals(y)为true ...

  6. EffectiveJava(9)覆盖equals是总要覆盖hashCode

    覆盖equals是总要覆盖hashCode 通过散列函数将集合中不相等的实例均匀的分布在所有可能的散列值上 1.把某个非零的常数值保存在一个名为result的int类型变量中 2.对于对象中每个关键域 ...

  7. 第9条:覆盖equals时总要覆盖hashCode

    在每个覆盖equals方法的类中,也必须覆盖hashCode方法.否则,会违反Object.hashCode的通用约定,从而导致该类无法结合所有基于散列的集合一起正常工作,包括HashMap,Hash ...

  8. 覆盖equals时总要覆盖hashCode

    本文涉及到的概念 1.为什么重载equals方法时,要重载hashCode函数;没有重载hashCode带来的问题 2.一个对象hashCode的生成规则       1.为什么重载equals方法时 ...

  9. 如何正确的覆盖equals和hashCode

    一.Object所有的非final方法 public boolean equals(Object obj) public native int hashCode() public String toS ...

随机推荐

  1. MDK —— configuration wizard

    学习RTX 的时候发现RTX的配置文件可以MDK的图形界面来配置,感觉这个非常好,直观.方便.健壮,可以避免程序员写入错误的数据.   参考: µVision User's Guide->Uti ...

  2. RegularExpressionValidator 常用

    RegularExpressionValidator 控件用于验证输入值是否匹配正则表达式指定的模式 属性: ControlToValidate="要验证的控件名称" Valida ...

  3. winfrom获得鼠标的坐标

    Point mouse = this.PointToScreen(Control.MousePosition);label1.Text = mouse.X.ToString() + ":&q ...

  4. [译]36 Days of Web Testing(一)

    [前言]最近负责的一次迭代发布中,一个小需求涉及前端JS改动,在测试这个需求的过程中忽略了浏览器兼容性测试,导致了一个线上bug.恶补下web测试,<36Days of web testing& ...

  5. "Invalid bound statement (not found): com.sitech.admin.dao.TbOpenAbilityInfoDao.findAbilityReadyUp"mybatis配置文件bug

    问题描述: 通常在正常启动某项工程后操作某个功能时抛出的bug: org.apache.ibatis.binding.BindingException: Invalid bound statement ...

  6. Ubuntu下su:authentication failure的解决办法

    $ su - rootPassword: su: Authentication failureSorry. 这时候输入 $ sudo passwd rootEnter new UNIX passwor ...

  7. 在keil中使用Astyle格式化你的代码的方法

    一个好的程序,不仅要有好的算法,同时也需要有良好的书写风格.写出好的算法不容易,但是好的代码风格却并不困难.提到格式化代码,Astyle是一个不能错过的工具,它可以方便的将程序代码格式化成自己想要的样 ...

  8. Android清除本地数据缓存代码

    /*  * 文 件 名:  DataCleanManager.java  * 描    述:  主要功能有清除内/外缓存,清除数据库,清除sharedPreference,清除files和清除自定义目 ...

  9. Spark安装和配置

    hadoop2的安装教程 Spark可以直接安装在hadoop2上面,主要是安装在hadoop2的yarn框架上面 安装Spark之前需要在每台机器上安装Scala,根据你下载的Spark版本,选择对 ...

  10. WIA Property Constant Definitions

    (@http://msdn.microsoft.com/en-us/library/windows/desktop/ms630202(v=vs.85).aspx): const DeviceID = ...