关于equals和hashCode
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的更多相关文章
- 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 ...
- JAVA中用堆和栈的概念来理解equals() "=="和hashcode()
在学习java基本数据类型和复杂数据类型的时候,特别是equals()"=="和hashcode()部分时,不是很懂,也停留了很长时间,最后终于有点眉目了. 要理解equals() ...
- 关于equals、hashcode和集合类的小结
一.首先明确一点:equals()方法和hashcode()方法是Object类里的方法. 查看源码可以知道,在Object类中equals(obj)方法直接返回的是 this == obj 的值. ...
- Object方法equals、hashCode
java知识背景: 1)hashCode()方法返回的是Jvm的32位地址 2)==比较的是对象在jvm中的地址 3)Object的equals()比较的就是jvm物理地址 4)比较2个对象使用equ ...
- Java中的equals和hashCode方法
本文转载自:Java中的equals和hashCode方法详解 Java中的equals方法和hashCode方法是Object中的,所以每个对象都是有这两个方法的,有时候我们需要实现特定需求,可能要 ...
- Java提高篇——equals()与hashCode()方法详解
java.lang.Object类中有两个非常重要的方法: 1 2 public boolean equals(Object obj) public int hashCode() Object类是类继 ...
- Java中==、equals、hashcode的区别与重写equals以及hashcode方法实例(转)
Java中==.equals.hashcode的区别与重写equals以及hashcode方法实例 原文地址:http://www.cnblogs.com/luankun0214/p/4421770 ...
- java中equals和hashCode方法的解析
解析Java对象的equals()和hashCode()的使用 前言 在Java语言中,equals()和hashCode()两个函数的使用是紧密配合的,你要是自己设计其中一个,就要设计另外一个.在多 ...
- Java实战equals()与hashCode()
一.equals()方法详解 equals()方法在object类中定义如下: 代码 public boolean equals(Object obj) { return (this == obj); ...
- 一次性搞清楚equals和hashCode
前言 在程序设计中,有很多的“公约”,遵守约定去实现你的代码,会让你避开很多坑,这些公约是前人总结出来的设计规范. Object类是Java中的万类之祖,其中,equals和hashCode是2个非常 ...
随机推荐
- [C++]访问控制与继承(public,protect,private) 有时间再整理!!!
http://www.cnblogs.com/chio/archive/2007/06/11/779408.html http://www.cnblogs.com/SelaSelah/archive/ ...
- Java 两个变量交换值
package test; public class Test { public static void main(String[] args) { int a, b; ...
- map each 工具函数
工具函数 即全局性的函数作用主要是提供比如字符串,数组,对象等方面的遍历 字符串工具 $.trim(str) 去除字符串两边的空格 遍历机制 map函数(function (obj,index){ ...
- hook_schema 小总结
1, primary key 可以带空格 做为下表 但field就不行 2, dev_node 仅为下标 'primary key' => array('nid'), 'foreign k ...
- 关于电脑玩MT以及多开的方法
方法是转的别人的首先感谢原创者!!上四开屏幕截图,因为小伙伴需要8张卡,所以我四个四个一起练.8开我的电脑估计都有压力,五开六开可能没问题,但是为了方便就四开,练完四个再练四个.图接下来说下多开模拟器 ...
- xcode ___gxx_personality_v0" 编译错误
xcode ___gxx_personality_v0" 编译错误 Undefined symbols for architecture i386: "___gxx_persona ...
- linux笔记:linux常用命令-目录和文件处理命令
命令格式: 命令 [-选项] [参数] 例: ls -la /etc 注意:个别命令的使用不遵循此格式. 目录处理命令:ls(列目录) 更多选项: -h 以kb等利于人阅读的方式取代字节显示文件大小 ...
- 【CodeForces 651B】Beautiful Paintings 排序+贪心
题目大意: 给定集合,对于任意一个的排列,记,求. 很明显每次搞出一个长度为的最长上升序列,然后把元素给删掉,答案增加. 直接暴力需要. 但是可以进行优化. 设有个,将个数从小到大排序,记为长度为的数 ...
- hdu----(2222)Keywords Search(trie树)
Keywords Search Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)T ...
- ARM的启动和中断向量表
启动的方式 对于S3C2440而言,启动的方式有两种,一是Nor Flash方式启动,二是Nand Flash方式启动. 使用Nor Flash方式启动 Nor Flash的地址范围如下 0x0000 ...