昨天写hibernate一对多查询的时候,用set集合来储存值,
我们都知道java中List集合是有序,可重复的,Set集合是无序,不可重复的。所以当时写这个查询的时候果断用Set来存值,但还是出现了值重复问题。
具体请看SF上提问:https://segmentfault.com/q/10...

我之前做的在for里对set有去重做法,但是有不足之处的。而这次在“一”的一方实体里重写Object基类的hashCode和equals方法做去重,感觉用的代码量减少了,又能提高效率,所以我这里对这两个方法做些自己的理解。

1、hashCode()和equals()是什么
hashCode和equals方法是在Object基类中,所以每个对象都可以重写这两个方法,
看看JDK中这两个方法源码

hashCode

/**
 * 查看hashCode源码是native修饰的, 说明这个方法是个原生函数,
 * 也就是说这个方法不是用java实现的,底层是用C/C++实现的
 * 返回哈希值
*/
 public native int hashCode();

equals

/*
用来比较两个引用所指向的对象内存地址是否一致
*/
 public boolean equals(Object obj) {
        return (this == obj);
    }

2、hashCode()和equals()关系是什么?
明白几个原则:
equals相等的两个对象,hashcode一定相等。
equals不相等的两个对象,hashcode不一定不相等。
hashcode不相等,那么equals是一定不等的。
hashcode相等,equals可能相等,也可能不等。
解释下第四个原则:
就好比hashcode像是一个词典目录里的按字母为索引查找字,查一个“Z”拼音开头的词,下面等查到“自己”,“自我”,“知道”等词,用equals就是判断这些词语当中是否有相等的词,“自己”和“知道”两个词不相等的,所以equals值不等,但它们同属于"Z"开头的词所以它们hashcode值相等。

3、为什么重写equals的同时也要重写hashCode呢?
这个问题不难解释,就像前面问题一样,hash算法是为了提高equals比较的效率而被发明出来的。这点在集合方面就能体现出来,就比如在List或Set集合里要比较是否有重复元素,当发现某个集合对象与另一个集合对象equals比较的结果相等时,则停止查找返回true值,反之,返回false值。但如果集合中有大量元素呢,假设一个集合A里有10万元素,而且另一个比较的对象B中还可能没有重复的值,则意味着其实不用比较我都知道两者不相同,但程序依然会对集合A里遍历10万元素然后和B进行逐一排查。。。又或者当我要加入第100001个数据我要验证前面元素有重复值,就要跟前面10万个元素一一比较,效率可想而知很低。

而哈希算法就是Java系统首先调用对象的hashCode()方法获得该对象的哈希码表,然后根据哈希码找到相应的存储区域,若哈希码相同在取得该存储区域内的每个元素与该对象进行equals方法比较,若不同也就没有继续比较的必要了,这对于数量较大的情况效率就提高了不少,重写hashcode方法,最主要就是保证操作的元素在同一个对象里且值都没有重复,若没有重写hashcode,可能会出现将对象引用不同,但元素完全相同的集合进行操作。

下面写一个例子

package demo;

public class override_HshCode_Equals {
    public int x;
    public int y;

    public override_HshCode_Equals(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;
    }

    /**
     * equals返回false就说明没有重复项可以添加
     * true是重复不能添加
     */
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;

        final override_HshCode_Equals heClass = (override_HshCode_Equals) obj;
        //这里就对实例化对象里的x,y进行比较
        if (x != heClass.x) {
            return false;
        }
        if (y != heClass.y) {
            return false;
        }
        return true;
    }
}

测试类

package demo;

import java.util.HashSet;

public class TestMain {
    public static void main(String[] args) {
        HashSet<override_HshCode_Equals> set = new HashSet<override_HshCode_Equals>();
        override_HshCode_Equals heClass1 = new override_HshCode_Equals(1, 1);
        override_HshCode_Equals heClass2 = new override_HshCode_Equals(5, 5);
        override_HshCode_Equals heClass3 = new override_HshCode_Equals(1, 1);
        set.add(heClass1);
        set.add(heClass2);
        set.add(heClass3);
        set.add(heClass1);
        System.out.println("set数量=" + set.size());
    }
}

结果如下

set数量=2

注:重写override_HshCode_Equals类的hashCode相当于自定义返回了一个“哈希码”,对比x,y值是否相等,先比较hashcode值,heClass1(3,3)==heClass3(3,3),所以它们的hashcode值相等,但是heClass2的x,y和heClass1,heClass3的x,y值不等,所以hashcode不等,equals一定不等,所以heClass2对象是可以add进去的。前面说了heClass1和heClass3的hashcode值相等,然后进入equals进行对比,发现两者的对象都是相同的,根据程序add循序来看,所以heClass1是可以add进去的,然后heClass3和后面add(heClass1)都为重复项,就不会添加

注释override_HshCode_Equals里的hashCode方法
结果如下

set数量=3

注:首先判断heClass1对象和heClass2对象的hashCode,此时Object中的hashCode方法返回的是对象本地内存地址的换算结果,不同的实例对象的hashCode是不相同的,同样因为heClass3和heClass1的hashCode也是不相等的,但是后面第四个添加中heClass1==heClass1的,所以最后set集合中只有heClass1,heClass2,heClass3这三个对象。

修改:override_HshCode_Equals heClass3 = new override_HshCode_Equals(1, 2);
结果如下

set数量=3

注:此时对比heClass1,heClass2,heClass3的x,y值都不相同,hashcode值不同,而第四个添加heClass1=heClass1,故添加不进去,set里有heClass1,heClass2,heClass3。

文章若有错误之处,欢迎指出。

参考网站:
http://www.cnblogs.com/ysch/p/4323889.html

暖心先森
287 声望16 粉丝

生命不息,编程不止!