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

来源:互联网 发布:爱淘宝1元天猫购物券 编辑:程序博客网 时间:2024/04/29 03:26

最近总是能够看到关于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冲突,采用链表法解决冲突。

0 0
原创粉丝点击