为什么我需要重写Java中的equals和hashCode方法?
最近我读了这个开发工程文件 。 这个文档是关于如何有效而正确地定义hashCode()
和equals()
,但是我无法弄清楚为什么我们需要重写这两个方法。
我怎样才能有效地实施这些方法?
Joshua Bloch谈到Effective Java
您必须在每个覆盖equals()的类中重写hashCode()。 如果不这样做将导致违反Object.hashCode()的一般合同,这将阻止您的类与所有基于散列的集合(包括HashMap,HashSet和Hashtable)一起正常运行。
让我们尝试用一个例子来理解它,如果我们重写equals()
而不重写hashCode()
并尝试使用一个Map
。
假设我们有一个这样的类,并且MyClass
两个对象如果它们的importantField
是相等的(用eclipse生成的hashCode()
和equals()
hashCode()
,它们是相等的)
public class MyClass { private final String importantField; private final String anotherField; public MyClass(final String equalField, final String anotherField) { this.importantField = equalField; this.anotherField = anotherField; } public String getEqualField() { return importantField; } public String getAnotherField() { return anotherField; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((importantField == null) ? 0 : importantField.hashCode()); return result; } @Override public boolean equals(final Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; final MyClass other = (MyClass) obj; if (importantField == null) { if (other.importantField != null) return false; } else if (!importantField.equals(other.importantField)) return false; return true; } }
重写仅equals
如果只有equals
被覆盖,那么当你调用myMap.put(first,someValue)
首先会散列到某个桶,当你调用myMap.put(second,someOtherValue)
它会散列到其他桶(因为它们有一个不同的hashCode
)。 所以,虽然他们是平等的,因为他们不是哈希到同一个桶,地图不能实现它,他们都留在地图上。
虽然如果我们重写hashCode()
,不需要重写equals()
,但是让我们看看在这种特殊情况下会发生什么事情,我们知道MyClass
两个对象如果它们的importantField
相等,但我们不覆盖equals()
。
只覆盖hashCode
想象一下,你有这个
MyClass first = new MyClass("a","first"); MyClass second = new MyClass("a","second");
如果您只重写hashCode
那么当您调用myMap.put(first,someValue)
时,首先需要计算其hashCode
并将其存储在给定的存储区中。 然后,当你调用myMap.put(second,someOtherValue)
它应该首先replace为第二个地图文档,因为它们是相等的(根据业务需求)。
但是问题在于equals没有被重新定义,所以当map second
迭代并且遍历桶时,看看是否存在一个使得second.equals(k)
为true的对象k
,它将不会find任何作为second.equals(first)
将是false
。
希望是明确的
HashMap和HashSet等集合使用对象的散列码值来确定对象应该如何存储在集合中,并再次使用散列码来帮助定位集合中的对象。
散列检索是一个两步过程。
- find合适的存储桶(使用hashCode())
- 在存储区中search正确的元素(使用equals())
这里是一个小例子,为什么我们应该忽略equals()和hashcode()。考虑一个Employee类,它有两个字段age和name。
public class Employee { String name; int age; public Employee(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public boolean equals(Object obj) { if (obj == this) return true; if (!(obj instanceof Employee)) return false; Employee employee = (Employee) obj; return employee.getAge() == this.getAge() && employee.getName() == this.getName(); } // commented /* @Override public int hashCode() { int result=17; result=31*result+age; result=31*result+(name!=null ? name.hashCode():0); return result; } */ }
现在创build一个类,将Employee对象插入HashSet并testing该对象是否存在。
public class ClientTest { public static void main(String[] args) { Employee employee = new Employee("rajeev", 24); Employee employee1 = new Employee("rajeev", 25); Employee employee2 = new Employee("rajeev", 24); HashSet<Employee> employees = new HashSet<Employee>(); employees.add(employee); System.out.println(employees.contains(employee2)); System.out.println("employee.hashCode(): " + employee.hashCode() + " employee2.hashCode():" + employee2.hashCode()); } } you will find result like this false employee.hashCode(): 321755204 employee2.hashCode():375890482
现在从hashcode()中删除注释行并执行相同的结果
true employee.hashCode(): -938387308 employee2.hashCode():-938387308
现在你能明白为什么如果两个对象被认为是相等的,他们的hashcode也必须是相等的? 否则,因为在Object类中默认的hashcode方法几乎总是为每个对象提供一个唯一的编号,即使equals()方法被覆盖,使得两个或多个对象被认为是平等的。 如果对象的哈希码没有反映出来,那么它们的平等程度如何也没有关系。 所以又一次:如果两个对象相等,那么它们的哈希码也必须相等。
您必须在每个覆盖equals()的类中重写hashCode()。 如果不这样做将导致违反Object.hashCode()的一般合同,这将阻止您的类与所有基于散列的集合(包括HashMap,HashSet和Hashtable)一起正常运行。
来自Effective Java ,Joshua Bloch
通过一致地定义equals()
和hashCode()
,可以提高类的可用性,作为基于散列的集合中的键。 正如hashCode的API文档所解释的那样:“为了散列表(如java.util.Hashtable
提供的散列表)的好处,支持此方法。
关于如何高效地实现这些方法的问题,最好的答案是build议您阅读Effective Java的第3章。
简单地说,Object中的equals方法检查引用是否相等,其中当属性相等时,类的两个实例仍然可以在语义上相等。 将对象放入使用equals和hashcode的容器(如HashMap和Set)时,这很重要。 假设我们有一个类:
public class Foo { String id; String whatevs; Foo(String id, String whatevs) { this.id = id; this.whatevs = whatevs; } }
我们使用相同的ID创build两个实例:
Foo a = new Foo("id", "something"); Foo b = new Foo("id", "something else");
没有压倒一切的平等,我们得到:
- a(b)是错误的,因为它们是两个不同的情况
- (a)是正确的,因为它是相同的实例
- b.equals(b)是真实的,因为它是相同的实例
正确? 好吧,如果这是你想要的。 但是让我们说,我们希望具有相同ID的对象是相同的对象,无论它是两个不同的实例。 我们重写equals(和hashcode):
public class Foo { String id; String whatevs; Foo(String id, String whatevs) { this.id = id; this.whatevs = whatevs; } @Override public boolean equals(Object other) { if (other instanceof Foo) { return ((Foo)other).id.equals(this.id); } } @Override public int hashCode() { return this.id.hashCode(); } }
至于实现equals和hashcode我可以推荐使用Guava的辅助方法
好吧,让我用非常简单的话来解释这个概念。
首先从更广泛的angular度来看,我们有集合,hashmap是集合中的数据结构之一。
为了理解为什么我们必须重写equals和hashcode方法,如果需要先了解什么是hashmap,那是什么。
哈希映射表是以数组方式存储数据的键值对的数据结构。 让我们说一个[],其中'a'中的每个元素是一个关键值对。
同样,上面的数组中的每个索引都可以链接列表,从而在一个索引处具有多个值。
现在为什么使用hashmap? 如果我们必须在一个大数组中进行search,然后search每个数组是否效率不高,那么哈希技术告诉我们,让我们用一些逻辑预处理数组,然后根据逻辑对数组进行分组,即哈希
例如:我们有1,2,3,4,5,6,7,8,9,10,11数组,我们应用一个散列函数mod 10,所以1,11将被组合在一起。 所以如果我们不得不在前面的数组中search11,那么我们将不得不迭代整个数组,但是当我们将它分组时,我们限制了迭代的范围,从而提高了速度。 用于存储所有上述信息的数据结构可以被认为是简单的二维数组
现在除了上面的hashmap还告诉它,它不会添加任何重复。 这是我们必须重写equals和hashcode的主要原因
所以当它说解释hashmap的内部工作时,我们需要找出hashmap有什么方法,它是如何遵循上面我解释的规则
所以hashmap的方法叫做put(K,V),根据hashmap,它应该遵循上述有效分配数组的规则,而不是添加任何重复的
所以put做的是,它将首先生成给定键的哈希码,以确定值应该进入哪个索引。如果在该索引处没有任何东西存在,则新的值将被添加到那里,如果在那里已经存在的那么应该在该索引的链表结束之后添加新的值。 但请记住,不应该根据散列映射的所需行为添加重复项。 所以可以说你有两个Integer对象aa = 11,bb = 11。 作为从对象类派生的每个对象,用于比较两个对象的默认实现是比较对象内的引用值和非值。 因此,在上述情况下,虽然语义上相等将会使平等testing失败,并且存在具有相同哈希码和相同值的两个对象的可能性,从而产生重复。 如果我们重写,那么我们可以避免添加重复。 你也可以参考细节工作
import java.util.HashMap; public class Employee { String name; String mobile; public Employee(String name,String mobile) { this.name=name; this.mobile=mobile; } @Override public int hashCode() { System.out.println("calling hascode method of Employee"); String str=this.name; Integer sum=0; for(int i=0;i<str.length();i++){ sum=sum+str.charAt(i); } return sum; } @Override public boolean equals(Object obj) { // TODO Auto-generated method stub System.out.println("calling equals method of Employee"); Employee emp=(Employee)obj; if(this.mobile.equalsIgnoreCase(emp.mobile)){ System.out.println("returning true"); return true; }else{ System.out.println("returning false"); return false; } } public static void main(String[] args) { // TODO Auto-generated method stub Employee emp=new Employee("abc", "hhh"); Employee emp2=new Employee("abc", "hhh"); HashMap<Employee, Employee> h=new HashMap<>(); //for (int i=0;i<5;i++){ h.put(emp, emp); h.put(emp2, emp2); //} System.out.println("----------------"); System.out.println("size of hashmap: "+h.size()); } }
hashCode()
:
如果你只是重写散列码方法,什么都不会发生。 因为它总是为每个对象返回新的hashCode
作为Object类。
equals()
:
如果只覆盖平等的方法,则a.equals(b)
为true,这意味着a和b的hashCode
必须相同,但不会发生。 因为你没有重写hashCode
方法。
注意:Object类的hashCode()
方法总是为每个对象返回新的hashCode
。
所以当你需要在基于哈希的集合中使用你的对象时,必须重写equals()
和hashCode()
。
为什么我需要重写Java中的equals和hashCode方法?
首先我们要了解等号的使用方法。
为了识别两个对象之间的差异,我们需要重写equals方法。
例如:
Customer customer1=new Customer("peter"); Customer customer2=customer1; customer1.equals(customer2); // returns true by JVM. ie both are refering same Object ------------------------------ Customer customer1=new Customer("peter"); Customer customer2=new Customer("peter"); customer1.equals(customer2); //return false by JVM ie we have two different peter customers. ------------------------------ Now I have overriden Customer class equals method as follows: @Override public boolean equals(Object obj) { if (this == obj) // it checks references return true; if (obj == null) // checks null return false; if (getClass() != obj.getClass()) // both object are instances of same class or not return false; Customer other = (Customer) obj; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) // it again using bulit in String object equals to identify the difference return false; return true; } Customer customer1=new Customer("peter"); Customer customer2=new Customer("peter"); Insteady identify the Object equality by JVM, we can do it by overring equals method. customer1.equals(customer2); // returns true by our own logic
现在hashCode方法很容易理解。
hashCode产生整数为了存储对象的数据结构像HashMap , HashSet 。
假设我们已经像上面那样覆盖了Customer
equals方法,
customer1.equals(customer2); // returns true by our own logic
在存储对象的时候使用数据结构。 如果我们使用内置哈希技术,对于上面的两个客户,它会生成两个不同的哈希码。 所以我们在两个不同的地方存储相同的对象。 为了避免这种问题,我们也应该基于以下原则重写hashCode方法。
- 不同的实例可能具有相同的哈希码。
- 相同的实例应该返回相同的哈希码。
Java提出一个规则
“如果两个对象使用Object类equals方法相等,那么hashcode方法应该给这两个对象相同的值。
所以,如果在我们的类中,我们重写equals,我们应该重写hashcode方法来休闲这个规则。 在Hashtable
中使用这两个方法equals
和hashcode
来存储值作为键值对。如果我们重写一个而不是另一个,有可能哈希表可能无法正常工作,如果我们使用这样的对象作为键。
为了在HashMap,Hashtable等集合中使用我们自己的类对象作为关键字,我们应该通过具有集合内部工作的意识来重写这两个方法(hashCode()和equals())。 否则,会导致错误的结果,我们并不期待。
Java中的Equals和Hashcode方法
它们是java.lang.Object类的方法,它是所有类的超类(自定义类以及在java API中定义的其他类)。
执行:
public boolean equals(Object obj)
public int hashCode()
public boolean equals(Object obj)
这个方法简单地检查两个对象引用x和y是否引用同一个对象。 即它检查是否x == y。
这是自反的:对于任何参考值x,x.equals(x)应该返回true。
它是对称的:对于任何引用值x和y,当且仅当y.equals(x)返回true时,x.equals(y)才返回true。
它是可传递的:对于任何引用值x,y和z,如果x.equals(y)返回true并且y.equals(z)返回true,则x.equals(z)应该返回true。
它是一致的:对于任何参考值x和y,x.equals(y)的多个调用始终返回true或始终返回false,前提是在对象的等于比较中没有使用的信息被修改。
对于任何非null的引用值x,x.equals(null)应该返回false。
public int hashCode()
此方法返callback用此方法的对象的哈希码值。 此方法以整数forms返回哈希代码值,并受到基于哈希的集合类(如Hashtable,HashMap,HashSet等)的支持。必须在覆盖equals方法的每个类中重写此方法。
hashCode的一般合约是:
只要在Java应用程序的执行过程中多次调用同一个对象,hashCode方法必须始终返回相同的整数,前提是在对象的equals比较中没有使用的信息被修改。
从应用程序的一次执行到同一应用程序的另一次执行,此整数不必保持一致。
如果两个对象按照equals(Object)方法相等,那么在两个对象的每一个上调用hashCode方法必须产生相同的整数结果。
如果两个对象根据equals(java.lang.Object)方法不相等,则不要求对两个对象中的每个对象调用hashCode方法都必须产生不同的整数结果。 但是,程序员应该意识到,为不相等的对象生成不同的整数结果可能会提高哈希表的性能。
相等的对象必须产生相同的哈希码,只要它们相等,不平等的对象不需要产生明显的哈希码。
资源:
JavaRanch的
图片
因为如果您不覆盖它们,您将使用Object中的默认实现。
考虑到实例的相等性和hascode值通常需要知道什么构成一个对象,他们通常需要在您的class级重新定义,以具有任何切实的意义。
添加@Lombo的答案
你什么时候需要重写equals()?
Object的equals()的默认实现是
public boolean equals(Object obj) { return (this == obj); }
这意味着只有当两个对象具有相同的内存地址时,才会认为两个对象相同。
但是,如果两个对象的一个或多个属性具有相同的值,则可能需要考虑两个对象相同(请参阅@Lombo答案中给出的示例)。
所以你会在这些情况下重写equals()
,你会给自己的平等条件。
我已经成功地实现了equals(),它工作的很好。那么为什么他们要求重写hashCode()呢?
好吧,只要你不在你的用户定义的类上使用基于“哈希”的集合 ,就没关系。 但是将来有一段时间你可能想要使用HashMap
或HashSet
,如果你不override
和“正确实现”hashCode() ,这些基于哈希的集合将无法正常工作。
重写只等于(@Lombo的答案除外)
myMap.put(first,someValue) myMap.contains(second); --> But it should be the same since the key are the same.But returns false!!! How?
首先,HashMap检查second
的hashCode是否与first
相同。 只有值相同时,才会检查相同桶中的相等性。
但是这里的hashCode对于这两个对象是不同的(因为它们有不同的内存地址 – 从默认实现)。 因此,它甚至不关心检查平等。
如果你的重写equals()方法中有一个断点,如果它们有不同的hashCode,它将不会进入。 contains()
检查hashCode()
,只有它们相同时,才会调用equals()
方法。
为什么我们不能让HashMap检查所有桶中的相等性? 所以我没有必要重写hashCode()!
那么你错过了基于哈希的集合点。 考虑以下几点:
Your hashCode() implementation : intObject%9.
以下是以桶forms存储的密钥。
Bucket 1 : 1,10,19,... (in thousands) Bucket 2 : 2,20,29... Bucket 3 : 3,21,30,... ...
说,你想知道地图是否包含密钥10.你想search所有的桶? 或者你想只search一个桶?
基于hashCode,你会发现,如果存在10,它必须存在于第一桶。所以只有桶1将被search!
考虑收集桶中的所有黑色的球。 你的工作是按照以下方式给这些球打上颜色,并用它来进行适当的游戏,
网球 – 黄色,红色。 为蟋蟀 – 白色
现在斗有黄色,红色和白色三种颜色的球。 而现在, 你做了着色只有你知道哪种颜色是哪个游戏。
着色球 – 哈希。 select游戏的球 – 等于。
如果你做的着色,有人select板球或网球球他们不会介意的颜色!
我正在寻找解释“如果你只是重写hashCode然后当你调用myMap.put(first,someValue)
它首先计算它的hashCode并将其存储在给定的桶。然后,当你调用myMap.put(first,someOtherValue)
根据地图文件,它应该先取代第二个,因为它们是相等的(根据我们的定义)“。 :
我想第二次当我们在myMap
中添加,那么它应该是像myMap.put(second,someOtherValue)
的“第二”对象myMap.put(second,someOtherValue)
使用Value对象时非常有用。 以下是波特兰模式库的摘录:
值对象的例子是像数字,date,钱和string的东西。 通常,它们是相当广泛使用的小物体。 他们的身份是基于他们的状态,而不是他们的对象身份。 这样,您可以拥有同一个概念值对象的多个副本。
所以我可以有一个对象的多个副本,代表date1998年1月16日。这些副本中的任何一个将相等。 对于像这样的小对象,创build新对象并移动它们往往更容易,而不是依靠单个对象来表示date。
值对象应该总是覆盖Java中的.equals()(或者在Smalltalk中)。 (还记得重写.hashCode()。)
假设你有类(A)聚集另外两个(B)(C),并且你需要存储(A)的实例在散列表内部。 默认实现只允许区分实例,但不能由(B)和(C)区分。 所以A的两个实例可以相等,但是默认不允许你以正确的方式比较它们。
方法equals和hashcode在对象类中定义。 默认情况下,如果equals方法返回true,那么系统将进一步检查哈希码的值。 如果2个对象的哈希码也是相同的,那么对象将被视为相同。 因此,如果只覆盖equals方法,那么即使重写的equals方法指示2个对象相等,系统定义的哈希码也不能指示2个对象相等。 所以我们也需要重写哈希码。
class A { int i; // Hashing Algorithm if even number return 0 else return 1 // Equals Algorithm, if i = this.i return true else false }
- put('key','value')将使用
hashCode()
来计算散列值,并使用equals()
方法来确定该值是否已经存在于Bucket中。 如果没有,将会添加其他的将被replace为当前值 - get('key')将使用
hashCode()
先查找Entry(bucket),然后用equals()
来查找Entry中的值
如果两者都被覆盖,
地图<A>
Map.Entry 1 --> 1,3,5,... Map.Entry 2 --> 2,4,6,...
如果等于不被覆盖
地图<A>
Map.Entry 1 --> 1,3,5,...,1,3,5,... // Duplicate values as equals not overridden Map.Entry 2 --> 2,4,6,...,2,4,..
如果hashCode没有被覆盖
地图<A>
Map.Entry 1 --> 1 Map.Entry 2 --> 2 Map.Entry 3 --> 3 Map.Entry 4 --> 1 Map.Entry 5 --> 2 Map.Entry 6 --> 3 // Same values are Stored in different hasCodes violates Contract 1 So on...
HashCode平等合同
- 两个等于相等方法的密钥应该生成相同的hashCode
- 生成相同哈希码的两个密钥不需要相等(在上面的例子中,所有偶数都生成相同的哈希码)
在下面的例子中,如果你在Person类中注释掉equals或hashcode的覆盖,这个代码将无法查找Tom的命令。 使用哈希码的默认实现会导致哈希表查找失败。
我下面是一个简单的代码,用来拉人的命令。 人被用作散列表中的一个键。
public class Person { String name; int age; String socialSecurityNumber; public Person(String name, int age, String socialSecurityNumber) { this.name = name; this.age = age; this.socialSecurityNumber = socialSecurityNumber; } @Override public boolean equals(Object p) { //Person is same if social security number is same if ((p instanceof Person) && this.socialSecurityNumber.equals(((Person) p).socialSecurityNumber)) { return true; } else { return false; } } @Override public int hashCode() { //I am using a hashing function in String.java instead of writing my own. return socialSecurityNumber.hashCode(); } } public class Order { String[] items; public void insertOrder(String[] items) { this.items=items; } } import java.util.Hashtable; public class Main { public static void main(String[] args) { Person p1=new Person("Tom",32,"548-56-4412"); Person p2=new Person("Jerry",60,"456-74-4125"); Person p3=new Person("Sherry",38,"418-55-1235"); Order order1=new Order(); order1.insertOrder(new String[]{"mouse","car charger"}); Order order2=new Order(); order2.insertOrder(new String[]{"Multi vitamin"}); Order order3=new Order(); order3.insertOrder(new String[]{"handbag", "iPod"}); Hashtable<Person,Order> hashtable=new Hashtable<Person,Order>(); hashtable.put(p1,order1); hashtable.put(p2,order2); hashtable.put(p3,order3); //The line below will fail if Person class does not override hashCode() Order tomOrder= hashtable.get(new Person("Tom", 32, "548-56-4412")); for(String item:tomOrder.items) { System.out.println(item); } } }
String class and wrapper classes have different implementation of equals()
and hashCode()
methods than Object class. equals() method of Object class compares the references of the objects, not the contents. hashCode() method of Object class returns distinct hashcode for every single object whether the contents are same.
It leads problem when you use Map collection and the key is of Persistent type, StringBuffer/builder type. Since they don't override equals() and hashCode() unlike String class, equals() will return false when you compare two different objects even though both have same contents. It will make the hashMap storing same content keys. Storing same content keys means it is violating the rule of Map because Map doesnt allow duplicate keys at all. Therefore you override equals() as well as hashCode() methods in your class and provide the implementation(IDE can generate these methods) so that they work same as String's equals() and hashCode() and prevent same content keys.
You have to override hashCode() method along with equals() because equals() work according hashcode.
Moreover overriding hashCode() method along with equals() helps to intact the equals()-hashCode() contract: "If two objects are equal, then they must have the same hash code."
When do you need to write custom implementation for hashCode()?
As we know that internal working of HashMap is on principle of Hashing. There are certain buckets where entrysets get stored. You customize the hashCode() implementation according your requirement so that same category objects can be stored into same index. when you store the values into Map collection using put(k,v)
method, the internal implementation of put() is:
put(k, v){ hash(k); index=hash & (n-1); }
Means, it generates index and the index is generated based on the hashcode of particular key object. So make this method generate hashcode according your requirement because same hashcode entrysets will be stored into same bucket or index.
而已!
The reason behind this: When your object fields can be null, implementing Object.equals can be a pain, because you have to check separately for null. Using Objects.equal lets you perform equals checks in a null-sensitive way, without risking a NullPointerException. Objects.equal("a", "a"); // returns true Objects.equal(null, "a"); // returns false Objects.equal("a", null); // returns false Objects.equal(null, null); // returns true
hashCode()
method is used to get a unique integer for given object. This integer is used for determining the bucket location, when this object needs to be stored in some HashTable
, HashMap
like data structure. By default, Object's hashCode()
method returns and integer representation of memory address where object is stored.
The hashCode()
method of objects is used when we insert them into a HashTable
, HashMap
or HashSet
. More about HashTables
on Wikipedia.org for reference.
To insert any entry in map data structure, we need both key and value. If both key and values are user define data types, the hashCode()
of the key will be determine where to store the object internally. When require to lookup the object from the map also, the hash code of the key will be determine where to search for the object.
The hash code only points to a certain "area" (or list, bucket etc) internally. Since different key objects could potentially have the same hash code, the hash code itself is no guarantee that the right key is found. The HashTable
then iterates this area (all keys with the same hash code) and uses the key's equals()
method to find the right key. Once the right key is found, the object stored for that key is returned.
So, as we can see, a combination of the hashCode()
and equals()
methods are used when storing and when looking up objects in a HashTable
.
笔记:
-
Always use same attributes of an object to generate
hashCode()
andequals()
both. As in our case, we have used employee id. -
equals()
must be consistent (if the objects are not modified, then it must keep returning the same value). -
Whenever
a.equals(b)
, thena.hashCode()
must be same asb.hashCode()
. -
如果你重写一个,那么你应该重写另一个。
http://parameshk.blogspot.in/2014/10/examples-of-comparable-comporator.html
IMHO, it's as per the rule says – If two objects are equal then they should have same hash, ie, equal objects should produce equal hash values.
Given above, default equals() in Object is == which does comparison on the address, hashCode() returns the address in integer(hash on actual address) which is again distinct for distinct Object.
If you need to use the custom Objects in the Hash based collections, you need to override both equals() and hashCode(), example If I want to maintain the HashSet of the Employee Objects, if I don't use stronger hashCode and equals I may endup overriding the two different Employee Objects, this happen when I use the age as the hashCode(), however I should be using the unique value which can be the Employee ID.
1) The common mistake is shown in the example below.
public class Car { private String color; public Car(String color) { this.color = color; } public boolean equals(Object obj) { if(obj==null) return false; if (!(obj instanceof Car)) return false; if (obj == this) return true; return this.color.equals(((Car) obj).color); } public static void main(String[] args) { Car a1 = new Car("green"); Car a2 = new Car("red"); //hashMap stores Car type and its quantity HashMap<Car, Integer> m = new HashMap<Car, Integer>(); m.put(a1, 10); m.put(a2, 20); System.out.println(m.get(new Car("green"))); } }
the green Car is not found
2. Problem caused by hashCode()
The problem is caused by the un-overridden method hashCode()
. The contract between equals()
and hashCode()
is:
- If two objects are equal, then they must have the same hash code.
-
If two objects have the same hash code, they may or may not be equal.
public int hashCode(){ return this.color.hashCode(); }
As per java documentation, developers should override both methods in order to achieve a fully working equality mechanism and it's not enough to just implement the equals() method.
If two objects are equal according to the equals(Object) method, then calling the hashcode() method on each of the two objects must produce the same integer result.
Overriding equals() alone would serve your needs when checking the equality of 2 normal objects and would also work with you when searching for an element inside a list .
However, you will fail when working with hashing data structures like: HashSet, HashMap, HashTable ..
This tutorial describes in details along with examples why it's necessary to always override equals() and hashcode() together. It's worth reading, check it.
Both the methods are defined in Object class. And both are in its simplest implementation. So when you need you want add some more implementation to these methods then you have override in your class.
For Ex: equals() method in object only checks its equality on the reference. So if you need compare its state as well then you can override that as it is done in String class.
Bah – "You must override hashCode() in every class that overrides equals()."
[from Effective Java, by Joshua Bloch?]
Isn't this the wrong way round? Overriding hashCode likely implies you're writing a hash-key class, but overriding equals certainly does not. There are many classes that are not used as hash-keys, but do want a logical-equality-testing method for some other reason. If you choose "equals" for it, you may then be mandated to write a hashCode implementation by overzealous application of this rule. All that achieves is adding untested code in the codebase, an evil waiting to trip someone up in the future. Also writing code you don't need is anti-agile. It's just wrong (and an ide generated one will probably be incompatible with your hand-crafted equals).
Surely they should have mandated an Interface on objects written to be used as keys? Regardless, Object should never have provided default hashCode() and equals() imho. It's probably encouraged many broken hash collections.
But anyway, I think the "rule" is written back to front. In the meantime, I'll keep avoiding using "equals" for equality testing methods 🙁