关于equals、hashcode和集合类的小结
一.首先明确一点:equals()方法和hashcode()方法是Object类里的方法。
查看源码可以知道,在Object类中equals(obj)方法直接返回的是 this == obj 的值。对于引用变量来说,这个式子判断的是变量中存储的内存地址是否一致。而hashcode()方法的出现,目的是为了提高哈希表的性能,也就是说,提供这样一个方法是为了提高依据hashcode进行存取的数据结构的性能的。
两者的关系:
在Object类的注释中可以看到两个原则:
两个类如果通过equals()方法比较返回的值为true,则这两个类调用hashcode()方法返回的整数值必须相等。
两个类如果通过equals()方法比较返回的值为false,则这两个类调用hashcode()方法返回的整数值建议不同(hashcode的初衷),可以相等。
故在Object类中:
equals()方法比较的方式是比较两个引用的内存地址,对于任何新 new 出来的对象来说,他们的内存地址都是不一样的。而Object类默认的hashcode计算方式和类的内存地址相关,所以他们的hashcode是不同的。
package com.njsteel.test; public class Test { public static void main(String[] args){ Object obj1 = new Object(); Object obj2 = new Object(); Object obj3 = obj1; obj1.hashCode();//14576877 obj2.hashCode();//12677476 obj3.hashCode();//14576877 obj1.equals(obj2);//false obj1.equals(obj3);//true } }
故,我们自定义的所有类,如果没有重写equals()方法和hashcode()方法,则都是满足上述的情况的。因为自定义的类都是Object类的子类。
二. 其次,Java提供的很多便捷类都会重写equals()和hashcode()方法。
这里只说明一下String类的情况。
为什么String类要重写equals()和hashcode()方法?
一般来说,当我们想判断两个String类对象的内容是否相同时,我们就会调用equals()方法,这是API文档告诉我们的。我们比较时,一般是两个不同对象的引用。这时候,即使String对象的内容相同,如果按照Object类里equals()的判断方法判断,返回的值始终都是false。
所以为了满足这样的需要,String类equals()方法重写添加了根据内容判断两个String对象内容是否相同的逻辑代码。
equals()被重写后,hashcode()自然也要被重写,因为重写的equals()方法破坏了Object类中规定的两者的关系。于是String类里的hashcode()方法也重写成了和String内容相关的样子。以满足原来的关系。
其他的类就可以按照这样的情况以此类推。。
三. 和某些集合类的关系
先说Set:
在看书的时候,对于Set的描述有:Set集合不允许包含相同的元素。
在Set里,相同的元素是指通过equals()方法返回值为true的两个对象。在调用Set的add()方法时,就会进行这个判断(add进来的元素会与Set集合里已经有的所有元素进行equals判断,如果有相同,则add()方法返回false,添加失败)。
对于HashSet类:
HashSet是Set接口的一个实现,自然也是不允许包含相同的元素。不过判断的方法和原始的Set有点不同。
往HashSet集合里添加元素时,它不仅会通过equals()方法判断,还要调用hashcode()方法判断。只有当两个元素equals相等,并且hashcode也相等时,两个元素才是相同的元素。当其中有一个方法比较显示两个元素不一样时,HashSet会将其添加。
对于HashMap类:
我们知道HashMap的Key值是不能重复的。其实HashMap的Key值是保存在一个Set()集合里的。所以,作为Key,至少他们的hashcode是不相同的。如果两个对象是相同的Key(equals相等,hashcode也相同),值会覆盖。
验证代码:
package com.njsteel.test; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; public class HashSetTest { public static void main(String[] args){ Set<MyString> set_1 = new HashSet<MyString>(); MyString myString1 = new MyString(); MyString myString2 = new MyString(); //添加一个元素 set_1.add(myString1); //添加一个equals返回true,但是hashcode不同的元素 System.out.println(myString1.equals(myString2)+"!!!"+myString1.hashCode()+"/"+myString2.hashCode()); System.out.println(set_1.add(myString2));//true,添加成功。 //添加一个元素 Set<MyTestObject> set_2 = new HashSet<MyTestObject>(); set_2.add(new MyTestObject()); //添加一个equals返回false,但是hashcode相同的元素。 System.out.println(set_2.add(new MyTestObject())+""+set_2.size()); Map<MyString,String> map = new HashMap<MyString,String>(); map.put(myString1, "1"); map.put(myString2, "2"); for(Object key : map.keySet()){ System.out.println(map.get(key)); } } } class MyString{ @Override public boolean equals(Object obj) { // TODO Auto-generated method stub return true; } @Override public int hashCode() { // TODO Auto-generated method stub return super.hashCode(); } } class MyTestObject{ @Override public boolean equals(Object obj) { // TODO Auto-generated method stub return false; } @Override public int hashCode() { // TODO Auto-generated method stub return 1; } }
欢迎讨论指正。。