关于equals()方法,jdk源码给出如下五点建议

Kirima ·
更新时间:2024-09-20
· 940 次阅读

一、趣谈 (1)、面试题

面试官:“你知道equals和==有什么区别吗?”
我的心理活动:这不是送分题吗?今早上刚背过,我要回答的有逼格一点,这样我就可以获得面试官的认可,得到这份工作,在不久的将来就会出任CEO,迎娶白富美,走向人生巅峰。
面试官:"喂,你知道吗?"
我:“不好意思面试官,刚才在总结思路。关于这个问题,我是阅读过jdk源码的,equals是比较对象内容,==是比较对象地址。”
面试官:“你确定吗?你看的那部分源码?”
我:“。。。。。。”
面试官:“好了,你回去等通知吧。”

(2)、一些思考

关于上面的面试题,我相信很多小伙伴会脱口而出,equals比内容,==比地址。那么真实情况是什么呢?让我们看看jdk源码来分析这个问题。
在这里插入图片描述
没错,这就是Object(the root of the class hierarchy)中equals()方法的源码,仅仅是使用"=="来判断。那么有为什么会有equals方法比较内容这一说法呢。究其根本是因为java的多态机制,我们的jdk中帮我们重写了一些类的equals方法(封装类,String等),所以看起来好像equals方法是比较内容一样。当我们使用自定义对象时,我们调用equals方法很明显是比较两个对象的地址。如果我们也想比较内容那么就要重写equals方法。

二、JDK源码中的建议 (1)、equals()的使用

我们重写equals方法大部分是因为要将自定义bean作为HashMap的key时。 我们接下来看看HashMap中常用的几个用到equals()的地方。

1. boolean containsKey(Object key); containsKey(Object key) public boolean containsKey(Object key) { return getNode(hash(key), key) != null; } hash(Object key) //根据对象的hashCode()方法返回一个int static final int hash(Object key) { int h; //可以从这里看出,HashMap允许key为null。 return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); } getNode(int hash, Object key)
HashMap源码
这段代码Josh Bloch(超级牛,感兴趣可以百度,可以说没有他,就没有这么优秀的作品)是作者之一,他的风格一向如此,喜欢在逻辑运算符上赋值,所以初看可能比较费劲。 注意看红框里的都是赋值操作,然后用赋的值本身去做逻辑运算。 我们可以看到判断一个key是否存在,(去掉一些边界判断) 首先查看hash()值是否相等,然后查看对象是否地址相同(即同一个对象)或调用equals()查看是否相等。 2.V get(Object key); public V get(Object key) { Node e; return (e = getNode(hash(key), key)) == null ? null : e.value; }

可以看到这个调用了刚才上面的代码。

3.public V put(K key, V value) ; public V put(K key, V value) { return putVal(hash(key), key, value, false, true); }

在这里插入图片描述
可以看到如出一辙的风范,这里的代码我们仅指出此次我们研究的重点步骤。

查看集合是否为空,如果为空开始扩容,然后new一个节点插入该集合。 查看给定key的hash()位置(HashMap是一个数组,每个元素放置在tab[(length - 1) & hash]位置,如果该位置已有元素则通过链表或树继续添加)是否有元素,如果没有new一个节点放入该位置。 如果该位置已经存在元素,则查看该位置的key值和要插入的key值是不是同一个对象(首先判断hash()值,然后判断是否是同一个对象即看==返回值,或者equals()方法返回true),因为null==null,所以可以看出HashMap中只允许一个键为null如果是同一个对象更新value值,如果不是进行遍历(链表或树),直到找到同一个key值,更新value,或者插入新节点(k,v)

我们总结一下,在HashMap中很多时候用到了判断两个key是否相等。他们的规则如下:

如果hashCode()不同,两个对象不同(所以说如果我们要重写equals()方法来自定义两个对象是否相等,必须要重写hashCode()方法,因为首先判断的是hash值。) 如果hashCode()相同,再查看==||equals(),如果==||equals()返回true则表示是同一个对象。 (2)、equals()重写的五点建议 /** * Indicates whether some other object is "equal to" this one. *

* The {@code equals} method implements an equivalence relation * on non-null object references: *

    *
  • It is reflexive: for any non-null reference value * {@code x}, {@code x.equals(x)} should return * {@code true}. *
  • It is symmetric: for any non-null reference values * {@code x} and {@code y}, {@code x.equals(y)} * should return {@code true} if and only if * {@code y.equals(x)} returns {@code true}. *
  • It is transitive: for any non-null reference values * {@code x}, {@code y}, and {@code z}, if * {@code x.equals(y)} returns {@code true} and * {@code y.equals(z)} returns {@code true}, then * {@code x.equals(z)} should return {@code true}. *
  • It is consistent: for any non-null reference values * {@code x} and {@code y}, multiple invocations of * {@code x.equals(y)} consistently return {@code true} * or consistently return {@code false}, provided no * information used in {@code equals} comparisons on the * objects is modified. *
  • For any non-null reference value {@code x}, * {@code x.equals(null)} should return {@code false}. *
*

* The {@code equals} method for class {@code Object} implements * the most discriminating possible equivalence relation on objects; * that is, for any non-null reference values {@code x} and * {@code y}, this method returns {@code true} if and only * if {@code x} and {@code y} refer to the same object * ({@code x == y} has the value {@code true}). *

* Note that it is generally necessary to override the {@code hashCode} * method whenever this method is overridden, so as to maintain the * general contract for the {@code hashCode} method, which states * that equal objects must have equal hash codes. * * @param obj the reference object with which to compare. * @return {@code true} if this object is the same as the obj * argument; {@code false} otherwise. * @see #hashCode() * @see java.util.HashMap */ public boolean equals(Object obj) { return (this == obj); }

在注释上使用ctrl+shifgt+空格,就会出现文档注释预览(idea2020版本做了更好的优化)
在这里插入图片描述
这个方法用来指示另一个对象是否"equals to"本对象。该方法实现对非空对象引用的一个等价关系:

反身性: 对于任意非空引用xx.quals(x)应该返回true。 对称性:对于任意非空引用值{x}和{y}x.equals(y)应返回true当且仅当y.equals(x)返回true 。 传递性:对于任意非空引用值x , y ,和z ,如果x.equals(y)返回truey.equals(z)返回true ,那么x.equals(z)应该返回true 。 一致性:对于任意非空引用值x和y ,如果没有修改equals()方法中设置的信息。多次调用x.equals(y)应该始终返回true或始终返回false , 对于任意非空引用x,x.equals(null)应该返回false

请注意,每当重写hashCode方法时,通常都需要重写该方法,以便维护hashCode方法的一般约定,该约定声明相等的对象必须具有相等的hash代码。

三、JDK源码中的优秀示例 (1)、String public boolean equals(Object anObject) { //如果地址相同,直接返回true if (this == anObject) { return true; } //判断是否是String的实例(值得注意的是null不是任何对象的实例,也就是说如果传入参数为null,直接返回false) if (anObject instanceof String) { String anotherString = (String)anObject; 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; } public int hashCode() { int h = hash; if (h == 0 && value.length > 0) { char val[] = value; //根据字符的unicode值,以31为基生成hashCode for (int i = 0; i < value.length; i++) { h = 31 * h + val[i]; } hash = h; } return h; } (2)、自定义bean模仿 class Student { private String name; public Student(String name) { this.name = name; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o instanceof Student) { Student student = (Student) o; if (student.name != null && student.name.equals(name)) { return true; } } return false; } @Override public int hashCode() { return name.hashCode(); } }

创作不易,请多多支持。后续会发布更多关于源码的文章,大家有兴趣学习可以关注查看更多优质文章:
git实现原理(从常见的操作来解释git的底层原理,再也不怯)
面试打怪升升级-被问烂的volatile关键字,这次我要搞懂它(深入到操作系统层面理解,超多图片示意图)


作者:Java新生代



equals 方法 jdk

需要 登录 后方可回复, 如果你还没有账号请 注册新账号
相关文章
Neysa 2020-04-10
651