文档章节

Java 中的equal()和hascode()方法

vshcxl
 vshcxl
发布于 2016/10/25 12:33
字数 2718
阅读 11
收藏 2

equals()反映的是对象或变量具体的值,即两个对象里面包含的值--可能是对象的引用,也可能是值类型的值。

而hashCode()是对象或变量通过哈希算法计算出的哈希值。

之所以有hashCode方法,是因为在批量的对象比较中,hashCode要比equals来得快,很多集合都用到了hashCode,比如HashTable。

 

两个obj,如果equals()相等,hashCode()一定相等。

两个obj,如果hashCode()相等,equals()不一定相等(Hash散列值有冲突的情况,虽然概率很低)。

所以:

可以考虑在集合中,判断两个对象是否相等的规则是:

第一步,如果hashCode()相等,则查看第二步,否则不相等;

第二步,查看equals()是否相等,如果相等,则两obj相等,否则还是不相等。

 

1、首先equals()和hashcode()这两个方法都是从object类中继承过来的。
equals()是对两个对象的地址值进行的比较(即比较引用是否相同)。
hashCode()是一个本地方法,它的实现是根据本地机器相关的。
2、Java语言对equals()的要求如下,这些要求是必须遵循的:
A 对称性:如果x.equals(y)返回是“true”,那么y.equals(x)也应该返回是“true”。
B 反射性:x.equals(x)必须返回是“true”。
C 类推性:如果x.equals(y)返回是“true”,而且y.equals(z)返回是“true”,那么z.equals(x)也应该返回是“true”。
D 一致性:如果x.equals(y)返回是“true”,只要x和y内容一直不变,不管你重复x.equals(y)多少次,返回都是“true”。
任何情况下,x.equals(null),永远返回是“false”;x.equals(和x不同类型的对象)永远返回是“false”。
3、equals()相等的两个对象,hashcode()一定相等;
反过来:hashcode()不等,一定能推出equals()也不等;
hashcode()相等,equals()可能相等,也可能不等。 

 

引用:http://blog.sina.com.cn/s/blog_59e0c16f0100xne7.html

1、为什么要重载equal方法?
答案:因为Object的equal方法默认是两个对象的引用的比较,意思就是指向同一内存,地址则相等,否则不相等;如果你现在需要利用对象里面的值来判断是否相等,则重载equal方法。
2、 为什么重载hashCode方法?
答案:一般的地方不需要重载hashCode,只有当类需要放在HashTable、HashMap、HashSet等等hash结构的集合时才会重载hashCode,那么为什么要重载hashCode呢?就HashMap来说,好比HashMap就是一个大内存块,里面有很多小内存块,小内存块里面是一系列的对象,可以利用hashCode来查找小内存块hashCode%size(小内存块数量),所以当equal相等时,hashCode必须相等,而且如果是object对象,必须重载hashCode和equal方法。
3、 为什么equals()相等,hashCode就一定要相等,而hashCode相等,却不要求equals相等?
答案:1、因为是按照hashCode来访问小内存块,所以hashCode必须相等。
2、HashMap获取一个对象是比较key的hashCode相等和equal为true。
之所以hashCode相等,却可以equal不等,就比如ObjectA和ObjectB他们都有属性name,那么hashCode都以name计算,所以hashCode一样,但是两个对象属于不同类型,所以equal为false。
4、 为什么需要hashCode?
1、 通过hashCode可以很快的查到小内存块。 2、通过hashCode比较比equal方法快,当get时先比较hashCode,如果hashCode不同,直接返回false。

 

hashCode()的作用

1.hashcode是用来查找的,如果你学过数据结构就应该知道,在查找和排序这一章有 例如内存中有这样的位置 0     1     2     3     4     5     6     7     而我有个类,这个类有个字段叫ID,我要把这个类存放在以上8个位置之一,如果不用hashcode而任意存放,那么当查找时就需要到这八个位置里挨个去找,或者用二分法一类的算法。 但如果用hashcode那就会使效率提高很多。 我们这个类中有个字段叫ID,那么我们就定义我们的hashcode为ID%8,然后把我们的类存放在取得得余数那个位置。比如我们的ID为9,9除8的余数为1,那么我们就把该类存在1这个位置,如果ID是13,求得的余数是5,那么我们就把该类放在5这个位置。这样,以后在查找该类时就可以通过ID除8求余数直接找到存放的位置了。

2.但是如果两个类有相同的hashcode怎么办那(我们假设上面的类的ID不是唯一的),例如9除以8和17除以8的余数都是1,那么这是不是合法的,回答是:可以这样。那么如何判断呢?在这个时候就需要定义   equals了。 也就是说,我们先通过   hashcode来判断两个类是否存放某个桶里,但这个桶里可能有很多类,那么我们就需要再通过   equals   来在这个桶里找到我们要的类。 那么。重写了equals(),为什么还要重写hashCode()呢? 想想,你要在一个桶里找东西,你必须先要找到这个桶啊,你不通过重写hashcode()来找到桶,光重写equals()有什么用啊 3。你要对A类排序,有两种方法,一种就是让A类实现comparabole结构并实现compareTo()方法,那么可以通过Collections.sort(List    list)对其进行排序 另一种方法:自己定义一个类B实现Comparator类并实现compare方法,然后通过Collections.sort(List list,B b)进行排序

hashCode()是用来产生哈希玛的,而哈希玛是用来在散列存储结构中确定对象的存储地址的,(这一段在   Java编程思想   中讲的很清楚的)象util包中的带hash的集合类都是用这种存储结构:HashMap,HashSet, 他们在将对象存储时(严格说是对象引用),需要确定他们的地址吧,而HashCode()就是这个用途的,一般都需要重新定义它的,因为默认情况下,由 Object类定义的 hashCode 方法会针对不同的对象返回不同的整数,这一般是通过将该对象的内部地址转换成一个整数来实现的,现在举个例子来说,就拿HashSet来说   ,在将对象存入其中时,通过被存入对象的hashCode() 来确定对象在HashSet中的存储地址,通过equals()来确定存入的对象是否重复,hashCode() ,equals()都需要自己重新定义,因为hashCode()默认前面已经说啦,而equals()   默认是比较的对象引用,你现在想一下,如果你不定义equals()的话,那么同一个类产生的两个内容完全相同的对象都可以存入Set,因为他们是通过equals()来确定的,这样就使得HashSet失去了他的意义,看一下下面这个: import java.util.*;
public class Test {   

  public static void main(String[] args) {      

   HashSet set = new HashSet();       

   for (int i = 0; i <= 3; i++){           

   set.add(new Demo1(i,i));                 

   }       

   System.out.println(set);     

   set.add(new Demo1(1,1));      

   System.out.println(set);      

   System.out.println(set.contains(new Demo1(0,0)));      

   System.out.println(set.add(new Demo1(1,1)));       

   System.out.println(set.add(new Demo1(4,4)));      

   System.out.println(set);  

   }

    private static class Demo1 {     

    private int value;              

    private int id;

        public Demo1(int value,int id) {        

        this.value = value;          

        this.id=id;      

   }

        public String toString() {          

   return " value = " + value;      

   }

        public boolean equals(Object o) {     

        Demo1 a = (Demo1) o;      

        return (a.value == value) ? true : false;  

       }

        public int hashCode() {           

        return id;      

   }    

}

} 你分别注释掉hashCode()和   equals()来比较一下他们作用就可以拉,关键要自己动手看看比较的结果你就可以记得很清楚啦

如果还不是很明确可以再看另一个例子:

import java.util.HashMap;

import java.util.Map;

public final class Test {

        public static void main(String[] args) {

        Map m = new HashMap();        

        m.put(new PhoneNumber(020, 12345678), "shellfeng");        

        System.out.println(m.get(new PhoneNumber(020, 12345678)));  

   }

        private static class PhoneNumber {           

        private short areaCode;

        private short extension;

        public PhoneNumber(int areaCode, int extension) {     

        this.areaCode = (short) areaCode;      

        this.extension = (short) extension;      

   }

        public boolean equals(Object o) {       

      if (o == this) {               

        return true;            

}          

       if (!(o instanceof PhoneNumber)) {       

          return false;        

     }         

          PhoneNumber pn = (PhoneNumber) o;           

          return pn.extension == extension && pn.areaCode == areaCode;    

     }

                public int hashCode() {          

           int result = 17;            

           result = 37 * result + areaCode;        

           result = 37 * result + extension;         

           return result;      

   }  

   }

}

还是那句话:你注释掉hashCode()比较一下他们作用就可以拉,关键要自己动手看看比较的结果你就可以记得很清楚啦

总结 hashCode()方法使用来提高Map里面的搜索效率的,Map会根据不同的hashCode()来放在不同的桶里面,Map在搜索一个对象的时候先通过hashCode()找到相应的桶,然后再根据equals()方法找到相应的对象.要正确的实现Map里面查找元素必须满足一下两个条件: (1)当obj1.equals(obj2)为true时obj1.hashCode()   ==   obj2.hashCode()必须为true (2)当obj1.hashCode() == obj2.hashCode()为false时obj.equals(obj2)必须为false

Java中的集合(Collection)有两类,一类是List,再有一类是Set。你知道它们的区别吗?前者集合内的元素是有序的,元素可以重复;后者元素无序,但元素不可重复。 那么这里就有一个比较严重的问题了:要想保证元素不重复,可两个元素是否重复应该依据什么来判断呢?这就是Object.equals方法了。 但是,如果每增加一个元素就检查一次,那么当元素很多时,后添加到集合中的元素比较的次数就非常多了。 也就是说,如果集合中现在已经有1000个元素,那么第1001个元素加入集合时,它就要调用1000次equals方法。这显然会大大降低效率。 哈希算法也称为散列算法,是将数据依特定算法直接指定到一个地址上。我们可以认为hashCode方法返回的就是对象存储的物理地址(实际可能并不是,例如:通过获取对象的物理地址然后除以8再求余,余数几是计算得到的散列值,我们就认为返回一个不是物理地址的数值,而是一个可以映射到物理地址的值)。 这样一来,当集合要添加新的元素时,先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置上。如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。所以这里存在一个冲突解决的问题。这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次。

本文转载自:http://www.cnblogs.com/ysw-go/p/4885164.html

vshcxl
粉丝 26
博文 283
码字总数 34755
作品 0
浦东
高级程序员
私信 提问
Guava库学习:Guava中Objects实用工具类的学习

链接地址:http://www.xx566.com/detail/128.html Java中的Object类是所有Java类的超类(也就是祖先),所有对象都实现Object类中的方法,在日常的工作中,我们经常需要重写其中的几个 方法,...

Realfighter
2014/11/28
0
0
覆盖hascode和toString,clone方法

hascode方法 覆盖equals时要覆盖hascode方法.另外覆盖hascode方法可以使的基于散列的集合,如hashtable,hashMap,hashSet分布的更均匀. 一般重写是将其关键域的hash加起来.这样能得到一个比较好...

流光韶逝
2017/01/12
0
0
HashMap简单实现原理的理解

正常情况下,Map的key和Value都可以用数组实现,一边ArrayList是key的keys,另一边是ArrayList value的values。 通过key获取value的方式是这样的values.get(keys.indexOf(key)). 然而,数组的...

110hxl
2014/02/17
0
0
用来理解 Java 编程语言的 8 个图表

很多时候,一张图比你说 1000 个字能更有效的说清楚一个问题。我们列举了 8 个关于 Java 语言的图表,或许可以让你对 Java 有着更深入的认识。 1. 字符串不变性(String Immutability) 下面的...

oschina
2013/09/23
7.8K
29
Java注解之Retention、Documented、Inherited介绍

最近研究多数据源问题,使用的是druid连接池,多数据源通过注解自动配置,使用这三个注解 @Retention @Documented @Inherited 自定义一个注解配置数据源 Retention注解:- Retention(保留)注...

品先
2018/07/13
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Qt编写自定义控件32-等待进度条控件

一、前言 在各种各样的执行任务界面,有时候需要比较多的时间,需要给出一个直观的等待进度条表示当前正在执行的进度,而不至于懵逼在那里,用户不会觉得程序死了还是干嘛了。 等待进度条有好...

飞扬青云
23分钟前
2
0
Packagist / Composer 中国全量镜像

还没安装 Composer 吗?请往下看如何安装 Composer 。 镜像用法 有两种方式启用本镜像服务: 系统全局配置: 即将配置信息添加到 Composer 的全局配置文件 config.json 中。见“方法一” 单个...

mdoo
24分钟前
1
0
mnist文件格式说明

根据官网 http://yann.lecun.com/exdb/mnist/ 的文件格式的定义 TRAINING SET LABEL FILE (train-labels-idx1-ubyte): [offset] [type] [value] [description] 0000 32 bit integer 0x00000......

冷基
42分钟前
2
0
DNS域名解析命令 —— host

命令host 用途: 使用域名服务器查询主机名字 语法: ]# host [选项] 主机名 [服务器] 常用选项: -t 指定查询的域名信息类型 A CERT DNAME IPSECKEY MX N...

迷失De挣扎
58分钟前
3
0
Ubuntu tty中文字符乱码

默认的tty只能显示一个字节,我们可以用setfont命令去改tty字体,但仅仅局限在一个字节内,不支持UTF-8多字节,所以我们就没办法使用汉字。但是我们可以使用FbTerm啊!FbTerm是支持中文显示的...

mbzhong
今天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部