美文网首页
HashCode和equals()

HashCode和equals()

作者: 米刀灵 | 来源:发表于2017-03-01 17:31 被阅读74次

    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认为是相同的元素。

    相关文章

      网友评论

          本文标题:HashCode和equals()

          本文链接:https://www.haomeiwen.com/subject/llccgttx.html