在研究这个问题之前,首先说明一下JDK 对equals(Object obj)和hashcode()这两个方法的定义和规范:
在Java 中任何一个对象都具备equals(Object obj)和hashcode()这两个方法,因为他们是在Object 类中定义的。equals(Object obj)方法用来判断两个对象是否“相同”,如果“相同”则返回true,否则返回false。hashcode()方法返回一个int 数,在Object 类中的默认实现是“将该对象的内部地址转换成一个整数返回”。接下来有两个个关于这两个方法的重要规范(我只是抽取了最重要的两个,其实不止两个):规范1:若重写equals(Object obj)方法,有必要重写hashcode()方法,确保通过equals(Object obj)方法判断结果为true 的两个对象具备相等的hashcode()返回值。说得简单点就是:“如果两个对象相同,那么他们的hashcode 应该相等”。不过请注意:这个只是规范,如果你非要写一个类让equals(Object obj)返回true 而hashcode()返回两个不相等的值,编译和运行都是不会报错的。不过这样违反了Java 规范,程序也就埋下了BUG。规范2:如果equals(Object obj)返回false,即两个对象“不相同”,并不要求对这两个对象调用hashcode()方法得到两个不相同的数。说的简单点就是:“如果两个对象不相同,他们的hashcode 可能相同”。根据这两个规范,可以得到如下推论:1、如果两个对象equals,Java 运行时环境会认为他们的hashcode 一定相等。2、如果两个对象不equals,他们的hashcode 有可能相等。3、如果两个对象hashcode 相等,他们不一定equals。4、如果两个对象hashcode 不相等,他们一定不equals。这样我们就可以推断Java 运行时环境是怎样判断HashSet 和HastMap中的两个对象相同或不同了。我的推断是:先判断hashcode 是否相等,再判断是否equals。测试程序如下: 首先我们定义一个类, 重写hashCode() 和equals(Object obj)方法1. class A {
2.3. @Override4. public boolean equals(Object obj) {5. System.out.println("判断equals");6. return false; //注意:这里:是返回了false7. }8. @Override9. public int hashCode() {10. System.out.println("判断hashcode");11. return 1;12. }13.}然后写一个测试类,代码如下:1. public class Test {2. public static void main(String[] args) {3. Map map = new HashMap();4. map.put(new A(), new Object());5. map.put(new A(), new Object());6.7. System.out.println(map.size());8. }9. }运行之后打印结果是:判断hashcode判断hashcode判断equals2可以看出,Java 运行时环境会调用new A()这个对象的hashcode()方法。其中:打印出的第一行“ 判断hashcode” 是第一次map.put(new A(), newObject())所打印出的。接下来的“判断hashcode”和“判断equals”是第二次map.put(new A(),new Object())所打印出来的。那么为什么会是这样一个打印结果呢?我是这样分析的:1、当第一次map.put(new A(), new Object())的时候,Java 运行时环境就会判断这个map 里面有没有和现在添加的new A()对象相同的键,判断方法:调用new A()对像的hashcode()方法,判断map 中当前是不是存在和new A()对象相同的HashCode。显然,这时候没有相同的,因为这个map 中都还没有东西。所以这时候hashcode 不相等,则没有必要再调用equals(Object obj)方法了。参见推论4(如果两个对象hashcode 不相等,他们一定不equals)2、当第二次map.put(new A(), new Object())的时候,Java 运行时环境再次判断,这时候发现了map 中有两个相同的hashcode(因为我重写了A 类的hashcode()方法永远都返回1),所以有必要调用equals(Objectobj)方法进行判断了。参见推论3(如果两个对象hashcode 相等,他们不一定equals),然后发现两个对象不equals(因为我重写了equals(Objectobj)方法,永远都返回false)。3、这时候判断结束,判断结果:两次存入的对象不是相同的对象。所以最后打印map 的长度的时候显示结果是:2。改写程序如下:1. import java.util.HashMap;2. import java.util.Map;3. class A {4. @Override5. public boolean equals(Object obj) {6. System.out.println("判断equals");7. return true; //注意:这里:是返回了true,跟上面的不同8. }9. @Override10. public int hashCode() {11. System.out.println("判断hashcode");12. return 1;13. }14.}15.public class Test {16. public static void main(String[] args) {17. Map map = new HashMap();18. map.put(new A(), new Object());19. map.put(new A(), new Object());20. System.out.println(map.size());21. }22.}运行之后打印结果是:判断hashcode判断hashcode判断equals1显然这时候map 的长度已经变成1 了,因为Java 运行时环境认为存入了两个相同的对象。原因可根据上述分析方式进行分析。以上分析的是HashMap,其实HashSet 的底层本身就是通过HashMap 来实现的,所以他的判断原理和HashMap 是一样的,也是先判断hashcode 再判断equals。