Java中的equals与hashCode方法(判断插入容器的内容是否重复)

最近总是能够看到关于equals与hashCode之间的关系的讨论, 虽然大概了解, 但还是缺乏一个系统的总结, 在这里就稍稍深入的介绍一下, 也作为知识的整合。


在看容器类是怎么使用equals与hashCode之前,先简单回顾下equals与hashCode的一些基础:

equals与hashCode两个方法均属于Object对象,equals根据我们的需要重写, 用来判断是否是同一个内容或同一个对象,具体是判断什么,怎么判断得看怎么重写,默认的equals是比较地址。

hashCode方法返回一个int的哈希码, 同样可以重写来自定义获取哈希码的方法。

equals判定为相同, hashCode一定相同。equals判定为不同,hashCode不一定不同。


equals与hashCode被一起提到, 经常都是会出现在容器对于插入的键值是否重复的讨论中。比如下面的set对于重复键的判断:

<span style="font-size:12px;">public class Test{
    public static void main(String [] args){
        List list=new ArrayList();
        list.add("a");
        list.add("b");
        list.add("a");
        Set set=new HashSet();
        set.add("a");
        set.add("b");
        set.add("a");
        System.out.println(list.size()+","+set.size());
    }
}</span>

输出为: 3,2

道理很简单, ArrayList可以是插重复的内容, 而Set不可以插重复的内容(String重写了equals与hashCode方法)。 那这个重复的内容, Set到底是怎么判断的呢?


对不可插入重复内容的容器而言,只用equals来判断是否是重复,是不够的。下面我们自定义了一个My对象,val相同的My对象被希望判定为相同的内容。然而实际并没有被视为是相同的。

/**
* @author csdn libertine1993 
*/
import java.util.*;
class My{
    public int val;
    My(int v){
        val = v;
    }
    //新手很容易以为重写了equals方法就足以判定是否重复
    @Override
    public boolean equals(Object my){
        System.out.println("equals called");//我们在equals方法内部加入输出的语句,当equals被调用则输出equals called
        return this.val == ((My)my).val;
    }
}
public class See{
    public static void main(String [] args){
        Set set=new HashSet();
        set.add(new My(0));
        set.add(new My(0));
        set.add(new My(0));
        System.out.println(set.size());//我们期望val相同的My对象会被判定为重复的内容,然而输出为3,三个val=0的对象均成功插入了。
    }
}

三个val = 0的My对象均插入了HashSet,set的size()返回了3, 说明hashSet并没有按照我们期望的将它们视为相同内容, 问题就出在我们没有重写hashCode方法,那么在判断的时候,我们先调用的是Object类中默认的hashCode, 它给了每一个My对象返回了不同的哈希码,那么每个对象都获得了一个空位置,自然就不需要调用equals方法,也就不会有equals called 的输出, 而HashSet中也成功插入了3个 val = 0的My对象。


如果我们重写了hashCode方法,结果就很不一样。

/**
* @author csdn libertine1993 
*/
import java.util.*;
class My{
    public int val;
    My(int v){
        val = v;
    }

    @Override
    public boolean equals(Object my){
        System.out.println("equals called");//在equals中加入了输出语句,调用会输出 equals called
        return this.val == ((My)my).val;
    }
    

    @Override
    public int hashCode(){
        System.out.println("hashCode called");//在hashCode中加入了输出语句,调用会输出 hashCode called
        return val;//直接以val的值作为哈希码返回
    }
}
public class Test{
    public static void main(String [] args){
        Set set=new HashSet();
        set.add(new My(0));
        set.add(new My(0));
        set.add(new My(0));
        System.out.println(set.size());
    }
}


输出为:

正如前面所说的, 先调用了hashCode,如果被占有再调用equals判断是否是重复,对第一个my对象而言,其hashCode返回0,此时0的位置空闲,直接插入不调用equals。对第二,三个My对象而言, 得到的hashCode均为0, 由于0处位置不为空,调用equals,判定为重复,后两个均插入失败,最终输出的size为1.


总结的结论是:对于自定义对象,在插入容器中需要做重复判断的时候,我们可能需要同时重写equals与hashCode方法,才能达到我们预期的重复判断。


另外补充一点需要注意的,这里对于hashSet而言,隐射到同一哈希码时,判定为相同内容则插入失败。与hashMap不同,hashMap隐射为同一哈希码之后,判定为hashCode冲突,采用链表法解决冲突。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值