一次性搞清晰equals和hashCode

来自版块 问答
485
1
媒介

在步伐计划中,有许多的“公约”,服从约定去实现你的代码,会让你避开许多坑,这些公约是前人总结出来的计划规范。
Object类是Java中的万类之祖,此中,equals和hashCode是2个非常紧张的方法。
这2个方法总是被人放在一起讨论。近来在看聚集框架,为了打底子,就决定把一些细枝末节清算掉。一次性搞清晰!
下面开始分析。

public boolean equals(Object obj)

Object类中默认的实现方式是 : return this == obj 。那就是说,只有this 和 obj引用同一个对象,才会返回true。
而昨们每每必要用equals来判定 2个对象是否等价,而非验证他们的唯一性。如许昨们在实现本身的类时,就要重写equals.
按照约定,equals要满意以下规则。
自反性: x.equals(x) 肯定是true
对null: x.equals(null) 肯定是false
对称性: x.equals(y) 和 y.equals(x)效果同等
通报性: a 和 b equals , b 和 c equals,那么 a 和 c也肯定equals。
同等性: 在某个运行时期间,2个对象的状态的改变不会不影响equals的决议效果,那么,在这个运行时期间,无论调用多少次equals,都返回雷同的效果。
一个例子
1 class Test 2 { 3     private int num; 4     private String data; 5  6     public boolean equals(Object obj) 7     { 8         if (this == obj) 9 return true;10 11         if ((obj == null) || (obj.getClass != this.getClass))12 return false;13
//能实行到这里,阐明obj和this同类且非null。 14 Test test = (Test) obj; 15 return num == test.num&& (data == test.data || (data != null && data.equals(test.data))); 16 } 17 18 public int hashCode 19 { 20 //重写equals,也必须重写hashCode。详细背面先容。
24 } 25 26 }
equals编写引导

Test类对象有2个字段,num和data,这2个字段代表了对象的状态,他们也用在equals方法中作为评判的依据。
在第8行,传入的比力对象的引用和this做比力,如许做是为了 save time ,节省实行时间,假如this 和 obj是 对同一个堆对象的引用,那么,他们肯定是qeuals 的。
接着,判定obj是不是为null,假如为null,肯定不equals,由于既然当前对象this能调用equals方法,那么它肯定不是null,非null 和 null固然不等价。
然后,比力2个对象的运行时类,是否为同一个类。不是同一个类,则不equals。getClass返回的是 this 和obj的运行时类的引用。假如他们属于同一个类,则返回的是同一个运行时类的引用。留意,一个类也是一个对象。
1、有些步伐员利用下面的第二种写法替换第一种比力运行时类的写法。应该制止如许做。
if((obj == null) || (obj.getClass != this.getClass))

return false; if(!(obj instanceof Test))

return false; // avoid 制止!它违背了公约中的对称原则。
比方:假设Dog扩展了Aminal类。
dog instanceof Animal 得到true
animal instanceof Dog 得到false
这就会导致
animal.equls(dog) 返回true
dog.equals(animal) 返回false
仅当Test类没有子类的时间,如许做才气包管是精确的。
2、按照第一种方法实现,那么equals只能比力同一个类的对象,差别类对象永久是false。但这并不是逼迫要求的。一样平常昨们也很少必要在差别的类之间利用equals。
3、在详细比力对象的字段的时间,对于根本值范例的字段,直接用 == 来比力(留意浮点数的比力,这是一个坑)对于引用范例的字段,你可以调用他们的equals,固然,你也必要处置惩罚字段为null 的环境。
4、并不总是要将对象的全部字段来作为equals 的评判依据,那取决于你的业务要求。好比你要做一个家电功率统计体系,假如2个家电的功率一样,那就有充足的依据以为这2个家电对象等价了,至少在你这个业务逻辑配景下是等价的,并不关心他们的价格啊,品牌啊,巨细等其他参数。
5、末了必要留意的是,equals 方法的参数范例是Object,不要写错!

public int hashCode

这个方法返回对象的散列码,返回值是int范例的散列码。
对象的散列码是为了更好的支持基于哈希机制的Java聚集类,比方 Hashtable, HashMap, HashSet 等。
关于hashCode方法,同等的约定是:
重写了euqls方法的对象必须同时重写hashCode方法。
假如2个对象通过equals调用后返回是true,那么这个2个对象的hashCode方法也必须返回同样的int型散列码
假如2个对象通过equals返回false,他们的hashCode返回的值答应雷同。(然而,步伐员必须意识到,hashCode返回独一无二的散列码,会让存储这个对象的hashtables更好地工作。)
在上面的例子中,Test类对象有2个字段,num和data,这2个字段代表了对象的状态,他们也用在equals方法中作为评判的依据。那么, 在hashCode方法中,这2个字段也要到场hash值的运算,作为hash运算的中心参数。这点很关键,这是为了服从:2个对象equals,那么 hashCode肯定雷同规则。
也是说,到场equals函数的字段,也必须都到场hashCode 的盘算。
合乎情理的是:同一个类中的差别对象返回差别的散列码。典范的方式就是根据对象的地点来转换为此对象的散列码,但是这种方式对于Java来说并不是唯一的要求的
的实现方式。通常也不是最好的实现方式。
相比 于 equals公认实现约定,hashCode的公约要求是很轻易明白的。有2个重点是hashCode方法必须服从的。约定的第3点,实在就是第2点的
细化,下面昨们就来看看对hashCode方法的同等约定要求。
第一:在某个运行时期间,只要对象的(字段的)变革不会影响equals方法的决议效果,那么,在这个期间,无论调用多少次hashCode,都必须返回同一个散列码。
第二:通过equals调用返回true 的2个对象的hashCode肯定一样。
第三:通过equasl返回false 的2个对象的散列码不必要差别,也就是他们的hashCode方法的返回值答应出现雷同的环境。
总结一句话:等价的(调用equals返回true)对象必须产生雷同的散列码。不等价的对象,不要求产生的散列码不雷同。

hashCode编写引导

在编写hashCode时,你必要思量的是,终极的hash是个int值,而不能溢出。差别的对象的hash码应该只管差别,制止hash辩论。
那么假如做到呢?下面是办理方案。
1、界说一个int范例的变量 hash,初始化为 非0 整数,8,17,20随你。
接下来让你以为紧张的字段(equals中权衡相称的字段)参入散列运,算每一个紧张字段都会产生一个hash分量,为终极的hash值做出贡献(影响)
紧张字段var的范例他天生的hash分量byte, char, short , int(int)varlong (int)(var ^ (var >>> 32))booleanvar?1:0float Float.floatToIntBits(var) double long bits = Double.doubleToLongBits(var);
分量 = (int)(bits ^ (bits >>> 32)); 引用范例 (null == var ? 0 : var.hashCode)

末了把全部的分量都总和起来,留意并不是简朴的相加。选择一个倍乘的数字31,到场盘算。然后不停地递归盘算,直到全部的字段都到场了。
int hash = 7;hash = 31 * hash + 字段1贡献分量;hash = 31 * hash + 字段2贡献分量;.....return hash;阐明,以下的内容是我在google上找到并翻译整理的,此中参加了本身的话和一些例子,便于明白,但我能包管这并不影响团体正确性。
编写过程中欣赏器挂掉了,又重写了一遍,不开心。不外为了写总结,让本身明白,应该的。
英文原文:
http://www.javaranch.com/journal/2002/10/equalhash.html

使用道具 举报

全部评论 1

谢谢小编
6 天前

热文

所属版块

您需要登录后才可以回帖 立即登录
说说你的想法......
0
1
0
返回顶部