谈谈Java中hashCode和equals方法

beverage-blue-sky.jpg

在Java学习中,hashCode和equals方法,是一个绕不开的话题。
这两个方法到底有什么作用?这两个方法什么时候需要重写?如何重写?
这篇文章,我们一起捋一捋Java中的hashCode和equals方法。

hashCode和equals是Object的方法

Object类方法.png

我们可以参考查阅JDK8的官方API文档:https://docs.oracle.com/javase/8/docs/api/index.html

Object类的描述

Class Object是类层次结构的根。每个类都有Object作为超类。所有对象(包括数组)都实现此类的方法。
所有的Java类默认继承Object类。只是隐式地默认集成。而没有使用extends关键字。

hashCode方法描述

返回对象的哈希码值。
提供此方法是因为哈希表的优势,例如HashMap中使用的哈希表。
hashCode的一般契约是:

  • 每当在执行Java应用程序期间多次在同一对象上调用它时,hashCode方法必须始终返回相同的整数,前提是不修改对象的equals比较中使用的信息。从应用程序的一次执行到同一应用程序的另一次执行,该整数不需要保持一致。

解读:
一次执行中的多次调用,同一个对象需要返回同一个hashcode值。
多次执行同一个对象,每次返回的hashcode值可以不一样。

  • 如果两个对象根据equals(Object)方法相等,则对两个对象中的每一个调用hashCode方法必须生成相同的整数结果。
  • 如果两个对象根据equals(java.lang.Object)方法不相等,则不需要在两个对象中的每一个上调用hashCode方法必须生成不同的整数结果。但是,程序员应该知道,为不等的对象生成不同的整数结果可能会提高散列表的性能。

尽可能合理,Object类定义的hashCode方法确实为不同的对象返回不同的整数。(这通常通过将对象的内部地址转换为整数来实现,但Java™编程语言不需要此实现技术。)

equals方法描述

表明某个其他对象是否“等于”此对象。
equals方法在非null对象引用上实现等价关系:

  • 自反性:对于任何非空引用值x,x.equals(x)应该返回true。
  • 对称性:对于任何非空引用值x和y,当且仅当y.equals(x)返回true时,x.equals(y)才应返回true。
  • 传递性:对于任何非空引用值x,y和z,如果x.equals(y)返回true而y.equals(z)返回true,则x.equals(z)应返回true。
  • 一致性:对于任何非空引用值x和y,x.equals(y)的多次调用始终返回true或始终返回false,前提是不修改在对象的equals比较中使用的信息。对于任何非空引用值x,x.equals(null)应返回false。

类Object的equals方法实现了对象上最具辨别力的等价关系;也就是说,对于任何非空引用值x和y,当且仅当x和y引用同一对象时,此方法才返回true(x == y的值为true)。

请注意,通常需要在重写此方法时覆盖hashCode方法,以便维护hashCode方法的常规协定,该方法声明相等的对象必须具有相等的哈希代码。

针对上述官方描述,我们继续查看equals方法:

public boolean equals(Object obj) {
        return (this == obj);
    }

我们追求的是业务上的内容相等

查看上述源码可知,在Object类中,equals比较的是对象地址是否相等,即是否是同一个对象,而不是比较值是否相等。

而我们业务中通常追求的是只是业务上的内容相等,所以我们业务中使用的类都是需要重写equals和hashCode方法的。

重写equals方法,同时必须重写hashCode方法

我们从Objec类官方API文档中,可知:
“如果两个对象根据equals(Object)方法相等,则对两个对象中的每一个调用hashCode方法必须生成相同的整数结果”。
所以我们重写equals方法的时候,为了满足这个原则,也必须同时重写hashCode方法。

public class ObjectEquals {
    public static void main(String[] args) {
        Student student1 = new Student("LeBron Jame", 12);
        Student student2 = new Student("LeBron Jame", 12);
        System.out.println(student1.equals(student2));
    }

    private static class Student {
        String name;
        Integer age;

        public Student(String name, Integer age) {
            this.name = name;
            this.age = age;
        }
    }
}

上述示例中,返回结果为false,因为Student类并没有重写equals方法,而是直接使用Object类中的equals方法,直接用==比较两个对象指向的地址是否一致,即是否是同一个对象,上述两个对象只是内容相同,但不是同一个对象,所以结果返回false。

public class ObjectEquals {
    public static void main(String[] args) {
        Student student1 = new Student("LeBron Jame", 12);
        Student student2 = new Student("LeBron Jame", 12);
        System.out.println(student1.equals(student2));
    }

    private static class Student {
        String name;
        Integer age;

        public Student(String name, Integer age) {
            this.name = name;
            this.age = age;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }
            if (this == obj) {
                return true;
            }
            Student student = (Student) obj;
            return (name.equals(student.name) && age.equals(student.age));
        }

    }
}

上述示例,重写Objec的equlas方法之后,只比较两个对象的内容是否相同,所以结果返回true。这就是业务上的内容相等。

System.out.println("对象student1的哈希值:"+student1.hashCode());
System.out.println("对象student2的哈希值:"+student2.hashCode());

上述示例中,我们增加打印两个对象的hashCode值,运行结果为:

对象student1的哈希值:1735600054
对象student2的哈希值:21685669

所以,如果我们只重写equals方法,但是不重写hashCode方法的话,两个对象的hashCode不一致。

@Override
        public int hashCode() {
            return name.hashCode() * 31 + age;
        }

上述示例,我们重写hashCode方法,运行结果:

对象student1的哈希值:147570189
对象student2的哈希值:147570189

可知,两个对象的hashCode值一致了。

总结

  • 重写equals方法的时候,必须同步重写hashCode方法。
  • 两个对象equals相等,hashCode值必然相等。
  • 两个对象hashCode值相等,equals不一定相等,因为可能存在哈希冲突。
  • hashCode方法,主要用于Java中的集合类,通过hashCode值进行高效率查找。

PS

  • 强烈推荐学习Java多看官方API文档,结合官方API文档看源码,追本溯源。
  • 强烈推荐阅读《Effective Java》,非常经典的一本好书。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,734评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,931评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,133评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,532评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,585评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,462评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,262评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,153评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,587评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,792评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,919评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,635评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,237评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,855评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,983评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,048评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,864评论 2 354