equals()和hashCode()是Object类的两个函数,重要性可见一斑,不过我们平时使用却未必能深入理解他们。本文从java doc触发,讲到它们与哈希表的关系,再到具体的实现,就我目前掌握的关于这两个函数进行一个梳理。

  一、Java Doc

  Java doc其实远不是只有在编程时查阅API才有用,很多时候它体现了Java的一些设计理念,当然这些理念需要好好分析才能理解。两个函数的具体doc文本可查,不予罗列,只说说重点:

  1. equals():

  a)该方法是在非空对象引用上实现相等关系,具有自反性、对称性、传递性和一致性。这里需要注意“非空”这个词,这说明,任何非空对象不可能equals一个空对象(null),在重写equals的时候,这一点很重要,不然极有可能NullPointerException。

  b)当equals函数被重写时通常有必要重写hashCode()函数,以维护hashCode() 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。这句话很抽象,后面解释。

  2. hashCode():

  a)支持此方法是为了提高哈希表的性能。这说明,该函数从设计上来说就是为了给哈希表使用的!

  b)hashCode协定,简而言之:如果用于equals比较的信息没有更改,那么hashCode必须返回相同的值;如果两个对象是equals的,那么hashCode必须相同;如果两个对象根据equals不等,那么它们hashCode也可以相等,只是不等的hashCode可以提高哈希表性能。

  二、Map、HashMap、HashSet等

  这几个数据结构与上述两个函数之间有非常重要的关系。以Set为例,Set中不能由相同的元素,因此比较就是用equals函数。也就是说,从通常的业务逻辑上来讲,要使用Set,就要重写equals函数,否则会出问题。Map类似。以如下类为例:

  

public class Student{
public int stuId;
public String stuName;
public boolean gender; public Student(int id,String name,boolean g)
{
stuId=id;
stuName=name;
gender=g;
} public static void main(String[] args)
{
Student stu1=new Student(123,"Tom",true);
Student stu2=new Student(123,"Tom",true);
System.out.println(stu1.equals(stu2));
}
}

  从业务逻辑上来讲,我们希望上述输出true,但事实上输出false。因此必须重写equals函数,如下:

    @Override
public boolean equals(Object o){
if(o==null)
return false;
if(o==this)
return true;
if(o.getClass!=getClass())
return false;
Student s=(Student) o;
return (stuId==e.stuId);
}

  但是加入上述代码仍然不够,假如main函数中有如下代码:

    public static void main(String[] args)
{
Student stu1=new Student(123,"Tom",true);
Student stu2=new Student(123,"Tom",true);
Set<Student> students=new HashSet<Student>();
students.add(stu1);
students.add(stu2);
System.out.println(students);
}

  我们是希望students中只有一个对象(Set的定义),然而输出了两个对象。问题何在?问题就在于HashSet的工作流程:为了提高效率,HashSet先通过计算hashCode得到对象应该插入的位置,如果该位置为空,就插入;如果该位置不为空,则比较新插入对象和已有对象是否equals,如果equals返回true,就不插入,如果返回false,则说明发生了冲突,使用解决冲突策略并把新对象插入。上述代码没有重写hashCode函数,两个对象返回不同的hashCode,插入时对应位置都是空的,直接插入了对象,换言之,Set中包含了两个equals返回true的对象,这违反了Set的定义,将给函数带来不可想象的后果。因此必须重写hashCode函数:

  

    @Override
public int hashCode()
{
final int PRIME=31;
int result=1;
result=PRIME*result+stuId;
return result;
}

  回过头看doc的说明:根据equals不相等的对象,其hashcode不一定非要不同(也即可以相同)。结合HashSet的工作流程,如果他们的hashcode相同,此时必然冲突,但是可以解决冲突后插入,所以不违反Set的定义但是却会影响性能。

  三、如何重写HashCode

  《Effective Java》中有介绍一种简单方法,具体不列,主要思想是使用所有参与equals比较的变量都与某个素数进行计算,使得不同对象计算出来尽量不同而且分散,这样可以减少冲突。关于此的博客网上很多,暂不作复述,待理解更深刻时整理。

  

关于equals和hashCode的更多相关文章

  1. How to implement equals() and hashCode() methods in Java[reproduced]

    Part I:equals() (javadoc) must define an equivalence relation (it must be reflexive, symmetric, and ...

  2. JAVA中用堆和栈的概念来理解equals() "=="和hashcode()

    在学习java基本数据类型和复杂数据类型的时候,特别是equals()"=="和hashcode()部分时,不是很懂,也停留了很长时间,最后终于有点眉目了. 要理解equals() ...

  3. 关于equals、hashcode和集合类的小结

    一.首先明确一点:equals()方法和hashcode()方法是Object类里的方法. 查看源码可以知道,在Object类中equals(obj)方法直接返回的是  this == obj 的值. ...

  4. Object方法equals、hashCode

    java知识背景: 1)hashCode()方法返回的是Jvm的32位地址 2)==比较的是对象在jvm中的地址 3)Object的equals()比较的就是jvm物理地址 4)比较2个对象使用equ ...

  5. Java中的equals和hashCode方法

    本文转载自:Java中的equals和hashCode方法详解 Java中的equals方法和hashCode方法是Object中的,所以每个对象都是有这两个方法的,有时候我们需要实现特定需求,可能要 ...

  6. Java提高篇——equals()与hashCode()方法详解

    java.lang.Object类中有两个非常重要的方法: 1 2 public boolean equals(Object obj) public int hashCode() Object类是类继 ...

  7. Java中==、equals、hashcode的区别与重写equals以及hashcode方法实例(转)

    Java中==.equals.hashcode的区别与重写equals以及hashcode方法实例  原文地址:http://www.cnblogs.com/luankun0214/p/4421770 ...

  8. java中equals和hashCode方法的解析

    解析Java对象的equals()和hashCode()的使用 前言 在Java语言中,equals()和hashCode()两个函数的使用是紧密配合的,你要是自己设计其中一个,就要设计另外一个.在多 ...

  9. Java实战equals()与hashCode()

    一.equals()方法详解 equals()方法在object类中定义如下: 代码 public boolean equals(Object obj) { return (this == obj); ...

  10. 一次性搞清楚equals和hashCode

    前言 在程序设计中,有很多的“公约”,遵守约定去实现你的代码,会让你避开很多坑,这些公约是前人总结出来的设计规范. Object类是Java中的万类之祖,其中,equals和hashCode是2个非常 ...

随机推荐

  1. J2EE 第二阶段项目之部署项目、分工安排

    SVN 先通过使用教程,和能够介绍了解svn. svn使用教程总结   ;   svn功能介绍. 分工安排:我的任务就是项目统计. 1 效益统计 1 教育效益统计表 (教育效益统计表,增,改,查看,查 ...

  2. kafka常用操作命令

    1.启动Kafka,其中">>/dev/null"表示将日志信息输出到"黑洞",其中"2>&1"表示将错误信息和前 ...

  3. PHP开发者常犯的MySQL错误

    PHP开发者常犯的MySQL错误   数据库是WEB大多数应用开发的基础.如果你是用PHP,那么大多数据库用的是MYSQL也是LAMP架构的重要部分. PHP看起来很简单,一个初学者也可以几个小时内就 ...

  4. gevent中如何实现长轮询

    浏览网页时,浏览器会传HTTP 请求到服务器,服务器会根据请求将网页的内容传给浏览器,但是在很多的情况下,使用者会需要看到最新的即时性资讯,例如观看股票市场行情,而在以前只能靠着重新载入网页才能获得最 ...

  5. _CRT_NONSTDC…与_CRT_SECURE…

    目录 第1章说明    1 1.1 _CRT_NONSTDC_NO_WARNINGS    1 1.2 _CRT_NON_CONFORMING_SWPRINTFS    2 1.3 _CRT_SECU ...

  6. java 集合(Map2)

    Map 接口的迭代方法: import java.util.*; public class ex12 { public static void main(String[] args) { Map< ...

  7. SAP MM事务代码清单

  8. 猫都能学会的Unity3D Shader入门指南

    https://onevcat.com/2013/07/shader-tutorial-1/ https://onevcat.com/2013/08/shader-tutorial-2/

  9. data structure

    转载请注明出处: 我所遇见的世界会更美好 我曾经逢人就说总是遇到这样那样错误无法解决,但是这些问题只要静下心来仔细思考根本没有解决不了的问题. 其实明明,超级超级简单,然而被老师讲的天花乱坠, 栈: ...

  10. div contenteditable placeholder

    contenteditable型的编辑框,实现placeholder的方式有两种 第一种,Css的实现方式: <!DOCTYPE html> <html lang="en& ...