首页>>后端>>java->Java对象的hashCode和equals方法

Java对象的hashCode和equals方法

时间:2023-12-07 本站 点击:0

参考资料

ref 1-阿里《Java开发手册》,「集合处理」章节

ref 2-《Effective Java》,第3章节,「第11条 覆盖equals时总要覆盖hashcode」

ref 3-为什么重写equals必须重写hashCode | Segmentfault

前言

根据阿里《Java开发手册》,对 Java 对象的 hashCodeequals 方法,有如下强制约定。

[强制] 关于 hashCodeequals 的处理,遵循如下规则

1)只要覆写 equals,就必须覆写 hashCode。

2)因为 Set 存储的是不重复的对象,依据 hashCode 和 equals 进行判断,所以 Set 存储的对象必须覆写这两个方法。

3)如果自定义对象作为 Map 的键,那么必须覆写 hashCode 和 equals。

说明:String 已经覆写 hashCode 和 equals 方法,所以我们可以愉快地使用 String 对象作为 key 来使用。

下面进行必要的补充分析。

equals保证可靠性,hashCode保证性能

equals 保证可靠性,hashCode 保证性能。

equalshashCode 都可用来判断两个对象是否相等,但是二者有区别

equals 可以保证比较对象是否是绝对相等,即「equals 保证可靠性」

hashCode 用来在最快的时间内判断两个对象是否相等,可能有「误判」,即「hashCode 保证性能」

两个对象 equals 为 true 时,要求 hashCode 也必须相等

两个对象 hashCode 为 true 时,equals 可以不等(如发生哈希碰撞时)

hashCode 的「误判」指的是

同一个对象的 hashCode 一定相等。

不同对象的 hashCode 也可能相等,这是因为 hashCode 是根据地址 hash 出来的一个 int 32 位的整型数字,相等是在所难免。

此处以向 HashMap 中插入数据(调用 put 方法,put 方法会调用内部的 putVal 方法)为例,对「equals 保证可靠性,hashCode 保证性能」这句话加以说明,putVal 方法中,判断两个 Key 是否相同的代码如下所示。

// putVal 方法if (p.hash == hash &&     ((k = p.key) == key || (key != null && key.equals(k))))...

在判断两个 Key 是否相同时,

先比较 hash(通过 hashCode 的高 16 位和低 16 位进行异或运算得出)。这可以在最快的时间内判断两个对象是否相等,保证性能。

但是不同对象的 hashCode 也可能相等。所以对满足 p.hash == hash 的条件,需要进一步判断。

继续,比较两个对象的地址是否相同,== 判断是否绝对相等,equals 判断是否客观相等。

自定义对象作为Set元素时

ref 1-自定义对象作为Map的键或Set元素,需要重写equals和hashCode方法 | CSDN

class Dog {    String color;    public Dog(String s) {        color = s;    }}public class SetAndHashCode {    public static void main(String[] args) {        HashSet<Dog> dogSet = new HashSet<Dog>();        dogSet.add(new Dog("white"));        dogSet.add(new Dog("white"));        System.out.println("We have " + dogSet.size() + " white dogs!");        if (dogSet.contains(new Dog("white"))) {            System.out.println("We have a white dog!");        } else {            System.out.println("No white dog!");        }    }}

运行程序,输出结果如下。

We have 2 white dogs!No white dog!

根据阿里《Java开发手册》可知,「因为 Set 存储的是不重复的对象,依据 hashCode 和 equals 进行判断,所以 Set 存储的对象必须覆写这两个方法」。将 Dog 代码修改为如下。

class Dog {    String color;    public Dog(String s) {        color = s;    }    //重写equals方法, 最佳实践就是如下这种判断顺序:    public boolean equals(Object obj) {        if (!(obj instanceof Dog))            return false;        if (obj == this)            return true;        return this.color == ((Dog) obj).color;    }    public int hashCode() {        return color.length();//简单原则    }}

此时,再运行程序,输出结果如下。

We have 1 white dogs!We have a white dog!

自定义对象作为Map的键和内存溢出

如下代码,自定义 KeylessEntry 对象,作为 Map 的键。

class KeylessEntry {    static class Key {        Integer id;        Key(Integer id) {            this.id = id;        }        @Override        public int hashCode() {            return id.hashCode();        }    }    public static void main(String[] args) {        Map m = new HashMap();        while (true){            for (int i = 0; i < 10000; i++){                if (!m.containsKey(new Key(i))){                    m.put(new Key(i), "Number:" + i);                }            }            System.out.println("m.size()=" + m.size());        }    }}

上述代码中,使用 containsKey(keyElement) 判断 Map 是否已经包含 keyElement 键值。containsKey 的关键代码如下所示,使用了 hashCodeequals 方法进行判断。

if (first.hash == hash &&     ((k = first.key) == key || (key != null && key.equals(k))))...

执行上述代码,因没有重写 equals 方法,导致 m.containsKey(new Key(i)) 判断总是 false,导致程序不断向 Map 中插入新的 key-value,造成死循环,最终将导致内存溢出。

原文:https://juejin.cn/post/7103010995970244645


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/java/18642.html