一般我们重写 equal() 时也会重写 hashCode(),而且 equal() 也经常被拿来和 == 做比较,所以我今天把它们三者放一起来说!
写这篇文章的主要原因是我在做 code review 的时候,发现有同学在错误的使用 HashSet,所以在写 HashSet 之前顺便先来说说 equals()、==、hashCode()。
equals()
equals() 的作用是用来判断两个对象是否相等。它定义在 JDK 的 Object 类中。源码如下:
public boolean equals(Object obj) { return (this == obj); }
Object 类中的 equals() 方法默认是通过判断两个对象的地址是否相等(即,是否是同一个对象)来区分它们是否相等的。
由于 Object 类是所有类基类,所以所有的 Java 类都有 equals() 方法。只不过默认的 equals() 方法,等价于 ==。
因此,我们通常会重写 equals() 方法。若某个类没有覆盖 equals() 方法,当通过它的 equals() 比较两个对象时,实际上是比较两个对象是不是同一个对象。这时,等价于通过“==”去比较这两个对象。当我们覆盖了类的 equals() 方法后,通过 equals() 方法比较两个对象是否相等。比较的是两个对象的内容相等,若相等则 equals() 方法返回 true;否则返回 fasle。
Java 的 equals() 有 5 大特性:
- 对称性:如果x.equals(y)返回是"true",那么y.equals(x)也应该返回是"true"。
- 反射性:x.equals(x)必须返回是"true"。
- 类推性:如果x.equals(y)返回是"true",而且y.equals(z)返回是"true",那么z.equals(x)也应该返回是"true"。
- 一致性:如果x.equals(y)返回是"true",只要x和y内容一直不变,不管你重复x.equals(y)多少次,返回都是"true"。
- 非空性:x.equals(null),永远返回是"false";x.equals(和 x 不同类型的对象)永远返回是"false"。
所以,我们在重写 equals() 方法的时候,一定要检验检验是否达到了上面的 5 大特性。这 5 大特性也被称为 equals() 的 5 大重写规则。
==
== 的作用是判断两个对象的地址是不是相等。即,判断两个对象是不试同一个对象。
面试的时候,基本上都会问到 equals 方法与‘==’运算符有什么区别?如果你回答的是:“equals 比较的是对象的内容,而‘==’比较的是对象的地址”。那么这个答案严格来说是错误的,至少是不完全正确吧。
通过前面的内容我们知道 equals 方法在 Object 中的实现也是直接的使用了‘==’运算符进行比较的。所以回答这个问题一定要严谨,加上前置条件。
hashCode()
hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个 int 整数。一般在 hash 集合中会用到。hashCode() 统一定义在 JDK 的 Object 中,这就意味着 Java 中的任何类都包含有 hashCode() 函数。
那么为什么重写 equals() 的同时还得重写 hashCode() 呢?就是因为 HashMap,Hashtable,HashSet 等集合在处理键对象的哈希码来计算存储位置的时,并不能保证 hash 不冲突。另外在 equals() 方法相等的时候,hashCode() 也不一定相等。
但是在使用散列表时,我们要求如果两个对象的 equals() 相等,那么它们的 hashCode() 的话, 值一定相同。如果两个对象 hashCode() 相等,它们的 equals() 并不一定相等。
因为在散列表中,hashCode() 相等,即两个键值对的哈希值相等。然而哈希值相等,并不一定能得出键值对相等。补充说一句:“两个不同的键值对,哈希值相等”,这就是哈希冲突。
我们知道 HashSet 是一个不允许存储重复元素的集合,但是如果你没有重写元素类的 equals() 和 hashCode() 的话,HashSet 基本上就失去了意义。这也是为什么很多同学误用 HashSet,但并没有用出自己想要的结果的原因。
关于 HashSet 的内容,我后面会单独来讲。有问题请关注我的微信公众号,给我留言!
: » 深度解读 Java 中的 equals()、==、hashCode()
原创文章,作者:bd101bd101,如若转载,请注明出处:https://blog.ytso.com/251950.html