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<Student , Integer> map = new HashMap<Student, Integer>();
Student t1 = new Student("s1","");
Student t2 = new Student("s1","");
System.out.println(t1.equals(t2));
map.put(t1, );
map.put(t2, );
System.out.println(map.size());
} } 运行结果
true
2

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

@Override
public int hashCode() {
int result = ;
int c = ;
c = this.name.hashCode();
result = * result+c;
c= this.id.hashCode();
result = *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方法没有用到的任何域。也不要试图从散列码中排除掉一个对象的关键部分来提高性能。

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

覆盖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. IbatisNet开发使用小结

    一.   介绍 平常做企业级应用,需求变化是经常的事,而很多基础代码重复也是很让人头疼的问题.所以很多人会使用一些ORM框架来增强项目的可维护性.可扩展性.IBatis.Net就是一个比较易用的ORM ...

  2. The Derivation About CNN and Antoencoder

    The Derivation About CNN and Antoencoder 公式推导 本人用latex写的关于CNN和autoencoder的推导,前向和反向传播的推导都有证明.pdf下载地址T ...

  3. ServletContextListener作用(转)

    ServletContext 被 Servlet 程序用来与 Web 容器通信.例如写日志,转发请求.每一个 Web 应用程序含有一个Context,被Web应用内的各个程序共享.因为Context可 ...

  4. Android基础之响应Menu键弹出菜单Demo

    对于Android我也不是很熟悉,只是学习一些基本内容就OK.所以写的内容也很简单.本Demo要实现的效果就点击Menu键将弹出一个菜单并响应点击菜单项事件. 一.废话少说直接上代码.其实就是重写两个 ...

  5. Python 入门教程 10 ---- Student Becomes the Teacher

    第一节 1 练习 1 设置三个的字典分别为lloyd,alice,tyler 2 对每一个的字典的key都设置为"name","homework" , &quo ...

  6. 微软开放技术发布开源的微软云服务器底盘管理器 (Chasis Manager) 软件

     发布于 2014-07-14 作者 陈 忠岳 今天,微软公司加入开放计算项目(OCP),贡献出硬件和软件规范,管理 API 和协议,机械 CAD 模型,以及电路板文件和 Gerbers(描述印刷 ...

  7. JDK 1.6.0和 6.0 有啥区别,JavaTM SE 6 的平台名字和版本号的说明(转)

    一直这么理解,今天才看到官方的解释,真是有点汗颜. 核心就是 6.0用于平台和产品的名字,而1.6.0用于开发者. 他们指的是同一个东西. 原文地址:http://java.sun.com/javas ...

  8. 如何优雅的输出PHP调试信息

    经常因为出现紧急bug而被老板骂的同事,为了更快的修复而直接利用线上的错误环境现场debug,并直接在页面上echo和dump.结果被老板发现了,又是一通臭骂.那么有没有什么办法更优雅的输出PHP调试 ...

  9. java多线程编程(1) 线程的基本知识

    在前面研究过多线程与进程的区别. 这里在稍微总结一下: 进程:程序动态的一次执行过程. 线程:可以只是程序员的一部分的执行过程 每个进程有多个线程组成,在java程序中,至少两个线程一个是垃圾回收线程 ...

  10. BZOJ4195 [Noi2015]程序自动分析(离散化+并查集)

    4195: [Noi2015]程序自动分析 Time Limit: 10 Sec  Memory Limit: 512 MB Submit: 689  Solved: 296 [Submit][Sta ...