在 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;}
hash: 散列算法,用来标志 文件/数据 的唯一性,相当于一种指纹。
hash表: 存储 hash值 。
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
既然已经知道 hashCode 与对象的存储地址没关系,那么我们想要获取对象地址咋办?
引入依赖:
org.openjdk.jol jol-core 0.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);
重写了 equals,不一定要重写 hashCode !!!
只有要将对象存入 散列表 ,并且已经重写了 equals,才需要重写 hashCode !!!
在 散列表 中,两个对象相等(equals 返回 true),那么他们的 hashCode 一定相同
先来简单回顾一下 HashMap 存储 key-value 的过程:
在 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 重写 HashMap 和 HashMap 的思路,我们就能自己重写这两个方法。
我们自定义对象 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
输出:
分析:
在上述案例的基础上,重写一下 hashCode
代码:
@Overridepublic int hashCode() {return name.hashCode();}
输出:
分析:
那么,我判断对象相等,需要其 name 和 age 都相等呢?
先重写 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
输出: