java

java中hashcode和equals详解以及代码示例

java中hashcode和equals详解以及代码示例

HashSet和HashMap一直都是JDK中最常用的两个类,HashSet要求不能存储相同的对象,HashMap要求不能存储相同的键。 那么Java运行时环境是如何判断HashSet中相同对象、HashMap中相同键的呢?当存储了“相同的东西”之后Java运行时环境又将如何来维护呢?

在研究这个问题之前,首先说明一下JDK对equals(Object obj)和hashcode()这两个方法的定义和规范: 在Java中任何一个对象都具备equals(Object obj)和hashcode()这两个方法,因为他们是在Object类中定义的。 equals(Object obj)方法用来判断两个对象是否“相同”,如果“相同”则返回true,否则返回false。 如果不重写equals方法, 就是为"==";

hashcode()方法返回一个int数,在Object类中的默认实现是“将该对象的内部地址转换成一个整数返回”。

接下来有两个个关于这两个方法的重要规范(我只是抽取了最重要的两个,其实不止两个): 规范1:若重写equals(Object obj)方法,有必要重写hashcode()方法,确保通过equals(Object obj)方法判断结果为true的两个对象具备相等的hashcode()返回值。说得简单点就是:“如果两个对象相同,那么他们的hashcode应该 相等”。

不过请注意:这个只是规范,如果你非要写一个类让equals(Object obj)返回true而hashcode()返回两个不相等的值,编译和运行都是不会报错的。不过这样违反了Java规范,程序也就埋下了BUG。 规范2:如果equals(Object obj)返回false,即两个对象“不相同”,并不要求对这两个对象调用hashcode()方法得到两个不相同的数。说的简单点就是:“如果两个对象不相同,他们的hashcode可能相同”。

根据这两个规范,可以得到如下推论: 1、如果两个对象equals,Java运行时环境会认为他们的hashcode一定相等。 2、如果两个对象不equals,他们的hashcode有可能相等。 3、如果两个对象hashcode相等,他们不一定equals。 4、如果两个对象hashcode不相等,他们一定不equals。
这样我们就可以推断Java运行时环境是怎样判断HashSet和HastMap中的两个对象相同或不同了。我的推断是:先判断hashcode是否相等,再判断是否equals。

接下来我们举个例子:

1、Set接口的contains方法,判断是否包含的依据是什么?

我查看了一下接口文档,里面是这样描述的:
该方法重写了Collection接口的contains方法

Returns true if this set contains the specified element. More formally, returns true if and only if this set contains an element e such that (o == null ? e==null : o.equals(e)).

也就是说,当且仅当
1:参数o,如果o为空,set中也有空时
2:参数o.equals(e),e为set中的一个元素
上述两种情况,会返回true。

代码验证

我自定义一个Dog对象,先不重写equals和hashCode,当Set中添加了两个白色的狗对象时,我们用contains方法去校验Set中是否包含白色的狗对象,会得到如下结果:

import java.util.HashSet;
import java.util.Set;

/**
 * created at 2019-06-08 17:24
 * @author wenbin
 */

class Dog {
    String color;

    public Dog(String s) {
        color = s;
    }
}

public class SetAndHashCode {

    public static void main(String[] args) {
        Set<Dog> dogSet = new HashSet<>();
        dogSet.add(new Dog("white"));
        dogSet.add(new Dog("white"));

        System.out.printf("we have %d white dogs", dogSet.size());
        System.out.println();

        if (dogSet.contains(new Dog("white"))) {
            System.out.println("Set has white dog!");
        }
        else {
            System.out.println("Set don't have white dog!");
        }

    }
}
/*
console results:
we have 2 white dogs
Set don't have white dog!
 */

这说明:
默认new一个新对象,会产生新的hash值。

可以验证一下:

public class SetAndHashCode {
    public static void main(String[] args) {
        System.out.println(new Dog("white").hashCode());
        System.out.println(new Dog("white").hashCode());
    }
}
/*
console results:
2000502626
704603837
 */

接下来,我们重新Dog类的equals方法,让类认为只要颜色一样,就算相等:

class Dog {
    String color;

    public Dog(String s) {
        color = s;
    }

    public String getColor() {
        return color;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Dog && this.color.equals(((Dog) obj).getColor())) {
            return true;
        }
        else {
            return false;
        }
    }
}

public class SetAndHashCode {

    public static void main(String[] args) {
        Set<Dog> dogSet = new HashSet<>();
        dogSet.add(new Dog("white"));
        dogSet.add(new Dog("white"));

        System.out.printf("we have %d white dogs", dogSet.size());
        System.out.println();

        if (dogSet.contains(new Dog("white"))) {
            System.out.println("Set has white dog!");
        }
        else {
            System.out.println("Set don't have white dog!");
        }

    }
}
/*
console results:
we have 2 white dogs
Set don't have white dog!
 */

但是结果并没有什么改变。对了,根据如果两个对象相等,则它们的hash值必须相等原则,我们还应该去重写hashCode方法:

@Override
    public int hashCode() {
        return this.color.hashCode();
    }

这时,控制台返回的结果终于变成了:

we have 1 white dogs
Set has white dog!

Set对重复元素,进行了去重。所以,添加了两次

        dogSet.add(new Dog("white"));
        dogSet.add(new Dog("white"));

后,dogSet.size()依旧为1。


本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 CN 许可协议。转载请注明出处!
文章若有侵权请立即与我联系, 我将及时处理
微信扫一扫,向我赞赏

微信扫一扫,向我赞赏

微信扫一扫,向我赞赏

支付宝扫一扫,向我赞赏

回复

This is just a placeholder img.
Title - Artist
0:00