Java中HashCode()和equals()的作用
引言
我们知道Java中的集合(Collection)大致可以分为两类,一类是List,再有一类是Set。
前者集合内的元素是有序的,元素可以重复;后者元素无序,但元素不可重复。
这里就引出一个问题:要想保证元素不重复应该依据什么来判断呢?
为什么要用hashCode()?
为了解决放入重复数据的问题,一开始开发者们想到了用Object.equals方法。
但是,很快他们发现如果每增加一个元素就检查一次,那么当元素很多时,后添加到集合中的元素比较的次数就非常多了。
也就是说,如果集合中现在已经有1000个元素,那么第1001个元素加入集合时,它就要调用1000次equals方法。这显然会大大降低效率。
于是,Java采用了哈希表的原理。哈希(Hash)实际上是个人名,由于他提出“哈希算法”的概念,所以就以他的名字命名了。
哈希算法也称为散列算法,哈希值也称为散列码,实际上就是将数据依照哈希算法直接指定到一个地址上。
初学者可以简单理解,hashCode方法实际上返回的就是对象存储的物理地址(实际可能并不是)。
这样一来,当集合要添加新的元素时,先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置上。
如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;
如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。
所以这里存在一个冲突解决的问题。这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次。
两个方法的作用
equals()作用:用于判断其他对象是否与该对象相同;
Object类中是这样定义equals():
public boolean equals(Object obj) {
return (this == obj);
}
很显然,在Object类原生代码中比较的是引用地址,但是需要提醒的一点是在String、Math、Integer、Double等封装类中都对equals()进行了不同程度的重写以满足其不同需要,例如在String类中:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = count;
if (n == anotherString.count) {
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
while (n– != 0) {
if (v1[i++] != v2[j++])
return false;
}
return true;
}
}
return false;
}
显然,在String类中的equals()比较的不再是引用对象的地址而是内容,在Java8种基本数据类型中equals()比较的都是内容,其实就是数值。
HashCode()作用:给不同对象返回不同的hash code值,相当于识别码;
使用HashCode()时应当符合以下三点:
- 在一个Java应用的执行期间,如果一个对象提供给equals做比较的信息没有被修改的话,该对象无论调用多少次hashCode(),必须始终返回同一个integer;
- 如果两个对象根据equals(Object)方法是相等的,那么调用二者各自的hashCode()必须产生同一个integer结果;
- 调用equals(java.lang.Object)方法结果不相等的两个对象,调用二者各自hashCode()不一定不相同,可能相同,可能不同。
重点
在集合查找时,使用hashcode无疑能大大降低对象比较次数,提高查找效率!
Java对象的eqauls方法和hashCode方法是这样规定的:
1、相等(相同)的对象必须具有相等的哈希码(或者散列码)。
2、如果两个对象的hashCode相同,它们并不一定相同。
可能的困惑
一、相等(相同)的对象必须具有相等的哈希码(或者散列码),为什么?
假设A与B这两个对象相等,即他们equals的结果为true,但他们各自的哈希码不相同,但他们要存入同一个HashMap时,有可能就会因为哈希码不同导致计算得出的HashMap内部数组位置索引不一样,那么A、B很可能同时存入同一个HashMap中,但我们知道HashMap是不允许存放重复元素的。
二、为什么两个对象的hashCode相同他们也不一定相同?
你的问题其实也是在说不同对象的hashCode有可能相同,产生这种结果的原因我个人觉得是由于“哈希算法”在生产哈希码时造成的,两个对象在某些方面具有高度一致性。正因为考虑到可能会出现这样的情况,所以HashMap在添加两个hashCode完全相同的对象时会在此哈希码指定的内部数组的位置索引处建立一个新的链表,然后将两个对象串起来放在该位置,这样就能在保证虽然hashCode相同仍能存入HashMap中,当然前提是他们调用equals()的返回值为false。
再补充一点,在业界中有一个专门的术语去描述这种现象,我们称之为哈希冲突,很显然,虽然哈希冲突是可以解决的,但没有人会希望经常看到它。
实际操作
在实际编写代码程序时,我们经常会被要求重写hashCode()和equals(),曾经我也对这个问题百思不得其解,但现在我也能向大家解释这其中的秘密了。
以HashSet为例,我们知道HashSet是继承Set接口,而Set接口由实现了Collection接口,HashSet中不允许出现重复值,而且元素的位置也是不确定的。
那么在这里介绍一下Java集合判断两个对象是否相等的规则是:
1.首先要判断两个对象的hashCode是否相等;
如果相等,进入第二步再判断;
如果不相等,那么认为两个对象也不相等,结束判断。
2.判断两个对象用equals()是否相等。
如果这次判断也相等,则认为两个对象相等;
如果不相等,那么认为两个对象也不相等。
为什么要进行两次判断呢?
可以不进行第一次的判断,但如果没有,实际使用效率会大大降低,尤其是在进行大量数据比较时。其实前面在介绍hashCode()时有过提及,即hashCode()相等时,equals()也可能不相等,所以我们就加上了第二条判断进行限制。总的来说,就是可以没有第一条判断,但必须要有第二条判断,但在实际开发中两条都最好写上,一旦出现大量数据需要判断时,仅靠equals()进行判断的话执行效率会大打折扣。
代码展示
package Exercise;
import java.util.HashSet;
public class e1 {
public static void main(String[] args) {
HashSet hs=new HashSet();
hs.add(new Student(1,"张三"));
hs.add(new Student(2,"李四"));
hs.add(new Student(3,"王麻子"));
hs.add(new Student(1,"张三"));
for (Object object : hs) {
System.out.println(object);
}
}
}
class Student{
int num;
String name;
Student(int num,String name){
this.name=name;
this.num=num;
}
public String toString(){
return num+":"+name;
}
}
运行结果:

为什么Hashset添加了相等的元素呢,这是不是和Hashset的原则违背了呢?回答是:没有。因为在根据hashCode()对两次建立的new Student(1,“张三 ”)对象进行比较时,生成的是不同的哈希码值,所以Hashset把他当作不同的对象对待了,当然此时的equals()方法返回的值也不等。
为什么会生成不同的哈希码值呢?原因就在于我们自己写的Student类并没有重新自己的hashCode()和equals()方法,所以在比较时,是继承的object类中的hashCode(),而object类中的hashCode()是一个本地方法,比较的是对象的地址(引用地址),使用new方法创建对象,两次生成的当然是不同的对象了,造成的结果就是两个对象的hashCode()返回的值不一样,所以Hashset会把它们当作不同的对象对待。
怎么解决这个问题呢?答案是:在Student类中重写hashCode()和equals()方法。
package Exercise;
import java.util.HashSet;
public class e1 {
public static void main(String[] args) {
HashSet hs=new HashSet();
hs.add(new Student(1,"张三"));
hs.add(new Student(2,"李四"));
hs.add(new Student(3,"王麻子"));
hs.add(new Student(1,"张三"));
for (Object object : hs) {
System.out.println(object);
}
}
}
class Student{
int num;
String name;
Student(int num,String name){
this.name=name;
this.num=num;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + num;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (num != other.num)
return false;
return true;
}
public String toString(){
return num+":"+name;
}
}
运行结果

可以看到重复元素的问题已经消除,根据重写的方法,即便两次调用了new Student(1,"张三"),我们在获得对象的哈希码时,根据重写的方法hashCode(),获得的哈希码肯定是一样的,当然根据equals()方法我们也可判断是相同的,所以在向hashset集合中添加时把它们当作重复元素看待了。
刚才使用的重写是用快捷键进行的,我们也可以手敲,不过写这么多就没必要了。
手敲重写代码:
public int hashCode(){
return num * name.hashCode();
}
public boolean equals(Object o){
Student s = (Student) o;
return num == s.num && name.equals(s.name);
}
做个总结
- 重点是equals,重写hashCode只是技术要求(为了提高效率);
- 为什么要重写equals呢?因为在Java的集合框架中,是通过equals来判断两个对象是否相等的;
- 在hibernate中,经常使用set集合来保存相关对象,而set集合是不允许重复的。在向HashSet集合中添加元素时,其实只要重写equals()这一条也可以。但当hashset中元素比较多时,或者是重写的equals()方法比较复杂时,我们只用equals()方法进行比较判断,效率也会非常低,所以引入了hashCode()这个方法,只是为了提高效率,且这是非常有必要的。
如果hashCode()这样写:
public int hashCode(){
return 1; //等价于hashcode无效
}
这样做的效果就是在比较哈希码的时候不能进行判断,因为每个对象返回的哈希码都是1,每次都必须要经过比较equals()方法后才能进行判断是否重复,这当然会引起效率的大大降低。
文章参考:
在Java中正确地使用equals()和hashCode()方法
Java中equals()与hashCode()方法详解
深入解析Java对象的hashCode和hashCode在HashMap的底层数据结构的应用
Java hashCode() 和 equals()的若干问题解答
Java中HashCode()和equals()的作用的更多相关文章
- java 中hashcode和equals 总结
一.概述 在Java中hashCode的实现总是伴随着equals,他们是紧密配合的,你要是自己设计了其中一个,就要设计另外一个.当然在多数情况下,这两个方法是不用我们考虑的,直 ...
- Java中hashcode,equals和==
hashcode方法返回该对象的哈希码值. hashCode()方法可以用来来提高Map里面的搜索效率的,Map会根据不同的hashCode()来放在不同的位置,Map在搜索一个对象的时候先通过has ...
- java中hashcode和equals的区别和联系
HashSet和HashMap一直都是JDK中最常用的两个类,HashSet要求不能存储相同的对象,HashMap要求不能存储相同的键. 那么Java运行时环境是如何判断HashSet中相同对象.Ha ...
- java中hashCode()与equals()详解
首先之所以会将hashCode()与equals()放到一起是因为它们具备一个相同的作用:用来比较某个东西.其中hashCode()主要是用在hash表中提高 查找效率,而equals()则相对而言使 ...
- 深入探究Java中hashCode()和equals()的关系
目录 一.基础:hashCode() 和 equals() 简介 equals() hashCode() 二. 漫谈:初识 hashCode() 与 equals() 之间的关系 三. 解密:深入理解 ...
- Java中hashCode、equals、==的区别
ref:http://www.cnblogs.com/skywang12345/p/3324958.html 1.==作用: java中的==用来判断两个对象的地址是否相等:当对象是基本数据类型时,可 ...
- java中hashcode()和equals()的详解
今天下午研究了半天hashcode()和equals()方法,终于有了一点点的明白,写下来与大家分享(zhaoxudong 2008.10.23晚21.36). 1. 首先equals()和hashc ...
- java中 hashCode() 和 equals()
1. 值类型是存储在内存中的栈,而引用类型的变量在栈中仅仅是存储引用类型变量的地址来自堆,而其本身则存储在栈中. 2. ==操作比较的是两个变量的值是否相等, 3. 对于引用型变量表示的是两个变量在堆 ...
- java中hashCode和equals什么关系,hashCode到底怎么用的
Object类的hashCode的用法:(新手一定要忽略本节,否则会很惨) 马 克-to-win:hashCode方法主要是Sun编写的一些数据结构比如Hashtable的hash算法中用到.因为ha ...
随机推荐
- 【前端 · 面试 】HTTP 总结(十二)—— URL 和 URI
最近我在做前端面试题总结系列,感兴趣的朋友可以添加关注,欢迎指正.交流. 争取每个知识点能够多总结一些,至少要做到在面试时,针对每个知识点都可以侃起来,不至于哑火. 引言 不知道有多少人是和我一样分不 ...
- RocketMQ原理分析&场景问题
硬核干货分享,欢迎关注[Java补习课]成长的路上,我们一起前行 ! <高可用系列文章> 已收录在专栏,欢迎关注! 一.RocketMQ的基本原理 RocketMQ基本架构图如下 从这个架 ...
- Vue系列-05-项目2
路飞学城项目 调整首页细节 固定头部 App.vue <style> body{ padding: 0; margin:0; margin-top: 80px; } </style& ...
- systemd.service — 服务单元配置
转载:http://www.jinbuguo.com/systemd/systemd.service.html 名称 systemd.service - 服务单元配置 大纲 service.servi ...
- 云原生的弹性 AI 训练系列之二:PyTorch 1.9.0 弹性分布式训练的设计与实现
背景 机器学习工作负载与传统的工作负载相比,一个比较显著的特点是对 GPU 的需求旺盛.在之前的文章中介绍过(https://mp.weixin.qq.com/s/Nasm-cXLtJObjLwLQH ...
- 算法入门 - 基于动态数组的栈和队列(Java版本)
之前我们学习了动态数组的实现,接下来我们用它来实现两种数据结构--栈和队列.首先,我们先来看一下栈. 什么是栈? 栈是计算机的一种数据结构,它可以临时存储数据.那么它跟数组有何区别呢? 我们知道,在数 ...
- 【硬件模块】RFIDSetting
The Octane SDK includes the core library by acting as a wrapper for extraction, modifying, and the a ...
- MongoDB用户创建
MongoDB采用基于角色的访问控制(RBAC)来确定用户的访问. 授予用户一个或多个角色,确定用户对MongoDB资源的访问权限和用户可以执行哪些操作. 用户应该只有最小权限集才能确保最小权限的系统 ...
- ReentrantReadWriteLock(读写锁)
为了提高性能,java提供了读写锁, 读锁: 在读的地方使用读锁,可以多个线程同时读. 写锁: 在写的地方使用写锁,只要有一个线程在写,其他线程就必须等待 例子: public static Read ...
- liunx上安装nacos
下载nacos wget https://github.com/alibaba/nacos/releases/download/1.4.1/nacos-server-1.4.1.tar.gz 启动服务 ...