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. HTML介绍、文档基本结构、meta标签、HTML标记的语法

    一.HTML的介绍 Hyper Text Mark-up Language 超文本标记语言,是一种描述性标记语言(不是编程语言),主要用于描述网页(可以有图像,文字,声音,等..)但没有交互性 HTM ...

  2. linux下在jar包中找类是否存在

    find /usr/lib -name "*.jar" -exec grep -Hsli 类名 {} \;

  3. STM8s窗口看门狗

    看看窗口看门狗的框图 从图里看出产生复位信号有2个方式: 1 WDGCR寄存器的T6 由1变0,也就是从此寄存器的值从0x40变成0x3F会产生复位信号: 2 当寄存器WDGCR的值大于WDGWR的时 ...

  4. android用shape给linearLayout设置边框,怎样只保留底部或顶部的边框,把其它三个方向的边框去掉呢?

    http://bbs.csdn.net/topics/390485215 这种方法只是两个颜色块相减而已 <?xml version="1.0" encoding=" ...

  5. 线性表 - 从零开始实现by C++

    参考链接:数据结构探险之线性表篇     线性表

  6. Nginx + Tomcat 配置

    Windows下Nginx的启动.停止等命令 在Windows下使用Nginx,我们需要掌握一些基本的操作命令,比如:启动.停止Nginx服务,重新载入Nginx等,下面我就进行一些简单的介绍.1.启 ...

  7. apk瘦身(包大小优化)

    最近太忙好久没有写随笔,放假才有空写写随笔. 最近在项目中一直在做包大小优化 随着业务需求的增加,包大小是忍不住的往上涨 为了提高应用市场下载转化率,一直在优化包大小 首先 分析你的apk中占比的主要 ...

  8. 【bzoj1019】汉诺塔

    [bzoj1019]汉诺塔 题意 传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1019 分析 思路1:待定系数+解方程 设\(f[n]\)为 ...

  9. 关于mvc3.0RadioButtonFor的使用

    以前本人没有使用过mvc,近期的话公司要求使用mvc进行开发项目,我不得不学习,我在这里分享下Html.RadioButtonFor的使用,例如 @Html.RadioButtonFor(d => ...

  10. EasyUI tab常用

    获取选中的tab的title var tab = $('#tab_Employee').tabs('getSelected'); var t=tab.panel('options').title; t ...