两个对象相等(==、equals、hashCode)详解
创始人
2024-03-03 02:04:43
0

目录

  • 1. == 和 equals
  • 2. hashCode
    • 1. hash 概述
    • 2. hashCode
      • 1. 概念
      • 2. 获取对象地址
  • 3. hashCode 与 equals
    • 1. 两者关系
    • 2. 重写 equals并 重写 hashCode
      • 1. 只重写 equals
      • 2. 重写 equals 并重写 hashCode
    • 3. 小结

1. == 和 equals

在 Java 中,判断两个对象是否相等,使用的是 ==equals

==equals 的区别:

  • == 判断两对象相等,是判断它们的地址是否相等

  • equals 未被重写,也是通过判断地址来判断相等,等同于 ==

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

    但,当我们的业务有特殊需求的时候,往往会 重写 equals,如:String 中的 equals 就被重写

        // 地址相等 => 相等// 字符串内容相同 => 相等public boolean equals(Object anObject) {if (this == anObject) {return true;}if (anObject instanceof String) {String anotherString = (String)anObject;// String底层是用 char value[]存储字符的int n = value.length;if (n == anotherString.value.length) {char v1[] = value;char v2[] = anotherString.value;int i = 0;while (n-- != 0) {if (v1[i] != v2[i])return false;i++;}return true;}}return false;}
    

2. hashCode

1. hash 概述

hash: 散列算法,用来标志 文件/数据 的唯一性,相当于一种指纹。

  • 特点:
    • 正向快速:明文 + hash算法 => 快速获取 hash值
    • 逆向困难:几乎不可能根据给定 hash值 推导出密文
    • 输入敏感:原始输入信息轻微不同就会导致 hash 值 有很大的区别
    • 避免冲突:两段不同的明文几乎不可能出现相同的hash值

hash表: 存储 hash值 。

2. hashCode

1. 概念

hashCode 并不是对象的地址!!!

  • 每个对象都有一个 hashCode,其代表的是,对象存入散列表(HashTableHashMapHashSet)的地址
  • 只有要将对象存入 散列表 的时候才会用到 hashCode

获取 hashCode :

        Object obj = new Object();System.out.println(obj);int hashCode = obj.hashCode();System.out.println("hashCode: " + hashCode);System.out.println("hashCode16进制: " + Integer.toHexString(hashCode));System.out.println("使用最初的hashCode算法:" + System.identityHashCode(hashCode));
java.lang.Object@1b6d3586
hashCode: 460141958
hashCode16进制: 1b6d3586
使用最初的hashCode算法:1163157884

通过 JVM Options 指定 hashCode 的生成策略: N的取值在 [0,4]之间的随机整数,当N不在此区间,就会使用默认的生成策略。

-XX:hashCode=N

2. 获取对象地址

既然已经知道 hashCode 与对象的存储地址没关系,那么我们想要获取对象地址咋办?

  • 引入依赖:

    org.openjdk.joljol-core0.16
    
    
  • VM.current().addressOf(obj) 获取对象地址

            Object obj = new Object();String str = new String("123");long strAddress = VM.current().addressOf(str);long objAddress = VM.current().addressOf(obj);
    

3. hashCode 与 equals

1. 两者关系

重写了 equals,不一定要重写 hashCode !!!

只有要将对象存入 散列表 ,并且已经重写了 equals,才需要重写 hashCode !!!

在 散列表 中,两个对象相等(equals 返回 true),那么他们的 hashCode 一定相同


先来简单回顾一下 HashMap 存储 key-value 的过程:

  • 计算 hash 值:key.hashCode()^( key.hashCode() >>>16 ) 计算出hash值
  • 如果 没发生hash碰撞(两个对象的hash值相同),就把对象直接存入桶中
    如果 发生了hash碰撞,就判断 key 是否相等,并使用 链表/红黑树 进行数据处理

HashMap 中判断两个对象相等的条件时:key相等 且 value相等

        public final boolean equals(Object o) {if (o == this)return true;if (o instanceof Map.Entry) {Map.Entry e = (Map.Entry)o;if (Objects.equals(key, e.getKey()) &&Objects.equals(value, e.getValue()))return true;}return false;}

所以,在计算对象的hash值,并将其装入散列表的时候,需要计算其 key的 hashCode ,也需要计算其 value的 hashCode

        public final int hashCode() {return Objects.hashCode(key) ^ Objects.hashCode(value);}

通过参考 HashMap 重写 HashMapHashMap 的思路,我们就能自己重写这两个方法。

2. 重写 equals并 重写 hashCode

1. 只重写 equals

我们自定义对象 User,重写了其 equals (使得 name 相等,两个对象就相等)。

代码:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {private String name;private Integer age;/*** @Description 名字相同也返回true* @param obj* @return boolean*/@Overridepublic boolean equals(Object obj) {if (obj == null || !(obj instanceof User)) return false;if (obj == this) return true;User user = (User) obj;return (user.name .equals(this.name));}public static void main(String[] args) {Set set = new HashSet<>();User user1 = new User("张三", 3);User user2 = new User("张三", 4);User user3 = new User("李四", 5);set.add(user1);set.add(user2);set.add(user3);System.out.println(user1.equals(user2));System.out.println(set);set.forEach( item -> {System.out.print(item.hashCode() + " ");});}
}
 

输出:

分析:

  • HashSet 不能装重复对象,而 user1user2 是同一对象(equals 返回 true),但是两者都被装入了 HashSet,这逻辑明显就是错误的
  • 这就是 equals 返回 true 的范围 > hashCode 相等的范围 导致的

2. 重写 equals 并重写 hashCode

在上述案例的基础上,重写一下 hashCode

代码:

    @Overridepublic int hashCode() {return name.hashCode();}

输出:

分析:

  • user1user2 是同一对象(equals 返回 true),所以,只装入了 user1
  • 这就是重写了两方法之后, equals 返回 true 的范围 = hashCode 相等的范围的结果

那么,我判断对象相等,需要其 nameage 都相等呢?

先重写 equals

    @Overridepublic boolean equals(Object obj) {if (obj == null || !(obj instanceof User)) return false;if (obj == this) return true;User user = (User) obj;return (user.name .equals(this.name) && user.age == this.age);}

hashCode 应该这样写:

    @Overridepublic int hashCode() {return name.hashCode() ^ age.hashCode();}

main 方法:

    public static void main(String[] args) {Set set = new HashSet<>();User user1 = new User("张三", 3);User user2 = new User("张三", 1);User user3 = new User("李四", 5);User user4 = new User("李四", 5);set.add(user1);set.add(user2);set.add(user3);set.add(user4);System.out.println(user1.equals(user2));System.out.println(user3.equals(user4));System.out.println(set);set.forEach( item -> {System.out.print(item.hashCode() + " ");});}
 

输出:

3. 小结

  • 重写了 equals 不一定要重写 hashCode
    • 在没有使用 散列表(HashTableHashMapHashSet)的时候,hashCode 就是一串没有人调用的整数
  • 在 散列表 中,两个对象相等(equals 返回 true),那么他们的 hashCode 一定相同
    • 一旦使用了散列表,并且重写了 equals,那么就必须重写 hashCode

相关内容

热门资讯

美国2年期国债收益率上涨15个... 原标题:美国2年期国债收益率上涨15个基点 美国2年期国债收益率上涨15个基...
汽车油箱结构是什么(汽车油箱结... 本篇文章极速百科给大家谈谈汽车油箱结构是什么,以及汽车油箱结构原理图解对应的知识点,希望对各位有所帮...
嵌入式 ADC使用手册完整版 ... 嵌入式 ADC使用手册完整版 (188977万字)💜&#...
重大消息战皇大厅开挂是真的吗... 您好:战皇大厅这款游戏可以开挂,确实是有挂的,需要了解加客服微信【8435338】很多玩家在这款游戏...
盘点十款牵手跑胡子为什么一直... 您好:牵手跑胡子这款游戏可以开挂,确实是有挂的,需要了解加客服微信【8435338】很多玩家在这款游...
senator香烟多少一盒(s... 今天给各位分享senator香烟多少一盒的知识,其中也会对sevebstars香烟进行解释,如果能碰...
终于懂了新荣耀斗牛真的有挂吗... 您好:新荣耀斗牛这款游戏可以开挂,确实是有挂的,需要了解加客服微信8435338】很多玩家在这款游戏...
盘点十款明星麻将到底有没有挂... 您好:明星麻将这款游戏可以开挂,确实是有挂的,需要了解加客服微信【5848499】很多玩家在这款游戏...
总结文章“新道游棋牌有透视挂吗... 您好:新道游棋牌这款游戏可以开挂,确实是有挂的,需要了解加客服微信【7682267】很多玩家在这款游...
终于懂了手机麻将到底有没有挂... 您好:手机麻将这款游戏可以开挂,确实是有挂的,需要了解加客服微信【8435338】很多玩家在这款游戏...