HashCode和equals()

Java中的equals方法和hashCode方法是Object中的,所以每个对象都是有继承这两个方法的,有时候我们需要实现特定需求,可能要重写这两个方法。

比如:HashMap、HashTable、HashSet,所以涉及到使用hash值进行优化存储的地方,都会用到hashCode。
1.首先调用对象的hashCode()方法获得该对象的哈希码表。
2.然后根据哈希吗找到相应的存储区域。
3.最后取得该存储区域内的每个元素与该对象进行equals方法比较。
这样就不用遍历集合中的所有元素就可以得到结论,可见HashSet集合具有很好的对象检索性能。

hash与equals的关系:
hashCode就好比字典里每个字的索引,equals()好比比较的是字典里同一个字下的不同词语。

  • 不管执行多少次获取hash值的操作,只要对象不变,那么hash值是固定的
  • 若两个对象equals方法返回为true,则其hash值也应该是一样的。
  • 若两个对象equals方法返回为false,则其hash值可能相同。

下面来看一下一个具体的例子:
RectObject对象:

package com.weijia.demo;  
  
public class RectObject {  
    public int x;  
    public int y;  
    public RectObject(int x,int y){  
        this.x = x;  
        this.y = y;  
    }  
    @Override  
    public int hashCode(){  
        final int prime = 31;  
        int result = 1;  
        result = prime * result + x;  
        result = prime * result + y;  
        return result;  
    }  
    @Override  
    public boolean equals(Object obj){  
        if(this == obj)  
            return true;  
        if(obj == null)  
            return false;  
        if(getClass() != obj.getClass())  
            return false;  
        final RectObject other = (RectObject)obj;  
        if(x != other.x){  
            return false;  
        }  
        if(y != other.y){  
            return false;  
        }  
        return true;  
    }  
}  

我们重写了父类Object中的hashCode和equals方法,看到hashCode和equals方法中,如果两个RectObject对象的x,y值相等的话他们的hashCode值是相等的,同时equals返回的是true;
下面是测试代码:

package com.weijia.demo;  
import java.util.HashSet;  
public class Demo {  
    public static void main(String[] args){  
        HashSet<RectObject> set = new HashSet<RectObject>();  
        RectObject r1 = new RectObject(3,3);  
        RectObject r2 = new RectObject(5,5);  
        RectObject r3 = new RectObject(3,3);  
        set.add(r1);  
        set.add(r2);  
        set.add(r3);  
        set.add(r1);  
        System.out.println("size:"+set.size());  
    }  
}  
  • 我们向HashSet中存入到了四个对象,打印set集合的大小。
    运行结果:size:2
    因为重写了RectObject类的hashCode方法,只要RectObject对象的x,y属性值相等那么他的hashCode值也是相等的,先比较hashCode的值,r1和r2对象的x,y属性值不等,所以他们的hashCode不相同的,所以r2对象可以放进去,但是r3对象的x,y属性值和r1对象的属性值相同的,所以hashCode是相等的,这时候在比较r1和r3的equals方法,因为他么两的x,y值是相等的,所以r1,r3对象是相等的,所以r3不能放进去了,同样最后再添加一个r1也是没有没有添加进去的,所以set集合中只有一个r1和r2这两个对象

  • 下面我们把RectObject对象中的hashCode方法注释,即不重写hashCode方法,再运行一下代码:
    运行结果:size:3
    这个结果也是很简单的,首先判断r1对象和r2对象的hashCode,因为Object中的hashCode方法返回的是对象本地内存地址的换算结果,不同的实例对象的hashCode是不相同的,同样因为r3和r1的hashCode也是不相等的,但是r1==r1的,所以最后set集合中只有r1,r2,r3这三个对象,所以大小是3

  • 下面我们把RectObject对象中的equals方法中的内容注释,直接返回false,放開hashCode方法的注釋,运行一下代码:
    运行结果:size:3
    这个结果就有点意外了,我们来分析一下:
    首先r1和r2的对象比较hashCode,不相等,所以r2放进set中,再来看一下r3,比较r1和r3的hashCode方法,是相等的,然后比较他们两的equals方法,因为equals方法始终返回false,所以r1和r3也是不相等的,r3和r2就不用说了,他们两的hashCode是不相等的,所以r3放进set中,再看r4,比较r1和r4发现hashCode是相等的,在比较equals方法,因为equals返回false,所以r1和r4不相等,同一r2和r4也是不相等的,r3和r4也是不相等的,所以r4可以放到set集合中,那么结果应该是size:4,但為什麼是3呢?
    其實HashSet首先是判断hashCode是否相等,不相等的话,直接跳过,相等的话,然后再来比较这两个对象是否相等或者这两个对象的equals方法,因为是进行的或操作,所以只要有一个成立即可,那这里我们就可以解释了,其实上面的那个集合的大小是3,因为最后的一个r1没有放进去,以为r1==r1返回true的,所以没有放进去了。所以集合的大小是3。

最后我们在来看一下hashCode造成的内存泄露的问题:看一下代码:

package com.weijia.demo;  
import java.util.HashSet;  
public class Demo {  
    public static void main(String[] args){  
        HashSet<RectObject> set = new HashSet<RectObject>();  
        RectObject r1 = new RectObject(3,3);  
        RectObject r2 = new RectObject(5,5);  
        RectObject r3 = new RectObject(3,3);  
        set.add(r1);  
        set.add(r2);  
        set.add(r3);  
        r3.y = 7;  
        System.out.println("删除前的大小size:"+set.size());  
        set.remove(r3);  
        System.out.println("删除后的大小size:"+set.size());  
    }  
}  

运行结果:
删除前的大小size:3
删除后的大小size:3
调用了remove删除r3对象,以为删除了r3,但事实上并没有删除,這樣多次这样操作之后,内存就爆了。因為在调用remove方法的时候,会先使用对象的hashCode值去找到这个对象,然后进行删除,但我们在修改了r3对象的y属性的值,又因为RectObject对象的hashCode方法中有y值参与运算,所以r3对象的hashCode就发生改变了,所以remove方法中并没有找到r3了,所以删除失败。即r3的hashCode变了,但是他存储的位置没有更新,仍然在原来的位置上,所以当我们用他的新的hashCode去找肯定是找不到了。


要点:
Object中的hashCode方法返回的是对象本地内存地址的换算结果。
HashSet判断要放入的元素是否相等:
1.hashcode是否相等,不相等放入。
2.如果hashcode相等,判断equals方法和==方法,如果都不相等放入。equals和==方法只要有一个相等,HashSet认为是相同的元素。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

友情链接更多精彩内容