欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java常见面试问题: equals()与hashCode()的使用

程序员文章站 2022-06-06 22:25:45
本篇文章有如下方面: ① equals()与‘==’的区别; ② equals()方法的重写规则(5条); ③ 为什么重写equals()的同时还需要重写hashCode(); ④ JDK 7中对hashCode()方法的改进; ⑤ Java API文档中关于hashCode()方法的规定; ⑥ 重... ......

1 equals()与‘==’的区别

默认情况下也就是从超类object继承而来的equals()方法与‘==’是完全等价的, 比较的都是对象的内存地址.

但我们可以重写equals()方法, 使其按照我们的需求的方式进行比较, 比如string类就重写了equals方法, 它比较的是字符的序列, 而不再是内存地址.

2 equals()方法的重写规则

自反性、对称性、传递性等都是 中的概念.

集合论>

(1) 自反性: 对于任何非null的引用值x, x.equals(x)应返回true.

(2) 对称性: 对于任何非null的引用值x与y, 当且仅当:y.equals(x)返回true时, x.equals(y)才返回true.

(3) 传递性: 对于任何非null的引用值x、y与z, 如果y.equals(x)返回true, y.equals(z)返回true, 那么x.equals(z)也应返回true.

(4) 一致性: 对于任何非null的引用值x与y, 假设对象上equals比较中的信息没有被修改, 则多次调用x.equals(y)始终返回true或者始终返回false.

(5) 对于任何非空引用值x, x.equal(null)应返回false.

3 为什么重写equals()的同时还需要重写hashcode()

这个问题主要和映射(map接口)相关. 我们知道map接口的类会使用到键(key)的哈希码, 当我们调用put()/get()方法操作map容器时, 都是根据key的哈希码来计算存储位置的, 因此如果我们对哈希码的获取没有相关保证, 就可能会得不到预期的结果.

在java中, 我们可以通过hashcode()方法获取对象的哈希码, 哈希码的值就是对象的存储地址, 这个方法在object类中声明, 因此所有的子类都含有该方法.

hashcode就是哈希码(或者散列码), 是由对象导出的一个整型值, 哈希码是没有规律的, 如果x与y是两个不同的对象, 那么x.hashcode()与y.hashcode()就不会相同 —— 只要x和y对象没有重写hashcode()方法, jvm规范中明确要求它们的散列码不会相同.

string(字符串)的哈希码是由字符串的内容导出的, 也就是string类中重写了hashcode()方法.

4 jdk 7中对hashcode()方法的改进

(1) java发布者希望我们使用更加安全的调用方式来返回散列码, 也就是使用null安全的java.util.objects.hashcode()方法, 这个方法的优点是如果参数为null, 就只返回0, 否则返回对象参数调用的hashcode的结果.

objects.hashcode()的源码如下:

public static int hashcode(object o) {
    return o != null ? o.hashcode() : 0;
}

(2) jdk 7中还提供了一个方法: java.util.objects.hash(object... objects), 当需要组合多个散列值时可以调用该方法, 比如:

public class model {
    private string name;
    private double salary;
    private int sex;
    // @override
    // public int hashcode() {
    //     return objects.hashcode(name) + new double(salary).hashcode() + new integer(sex).hashcode();
    // }

    @override
    public int hashcode() {
        return objects.hash(name, salary, sex);
    }
}

扩展: 如果我们提供的是一个数值类型的变量, 那么我们可以调用arrays.hashcode()方法来计算它的散列码, 这个散列码是由数组中各个元素的散列码组成的.

5 java api文档中关于hashcode()方法的规定

—— 内容摘自《java深入解析》.

(1) 在java应用程序执行期间, 如果在equals()方法中涉及到的信息没有被修改, 那么在同一个对象上多次调用hashcode()方法时必须一致地返回相同的整数. 如果多次执行同一个应用程序时, 不要求该整数必须相同.

(2) 如果两个对象通过调用equals()方法是相等的, 那么这两个对象调用hashcode()方法必须返回相同的整数.

(3) 如果两个对象通过调用equals()方法是不相等的, 不要求这两个对象调用hashcode()方法必须返回不同的整数. 但是开发人员应该意识到: 对不同的对象产生不同的hash值可以提高哈希表的性能.

6 重写equals()方法时推荐使用getclass(), 而不是instanceof

在重写equals()方法时, 一般推荐使用getclass()来进行类型判断, 而不是使用instanceof关键字.

除非所有的子类有统一的语义才使用instanceof, 统一的语义就是说, 不同的子类在equals()方法中比较的内容相同.

我们知道, instanceof关键字的作用是判断其左边对象是否为其右边类型的实例, 返回boolean类型的数据, 它多用来判断继承关系中的某个子类的实例是否为父类的实现.

7 编写一个完美的equals()方法的建议

—— 摘自《java核心技术 第一卷:基础知识》.

(1) 显式参数命名为otherobject, 稍后需要将它转换成另一个叫做other的变量 (参数名命名, 强制转换请参考下一条建议);

(2) 将otherobject转换为相应的类类型变量: classname other = (classname) otherobject;;

(3) 检测this与otherobject是否引用同一个对象: if(this == otherobject) return true; —— 存储地址相同, 肯定是同个对象, 直接返回true;

(4) 检测otherobject是否为null , 如果为null, 返回false: if(otherobject == null) return false;;

(5) 比较this与otherobject是否属于同一个类(视需求而选择):

① 如果equals的语义(可以理解为equals比较的内容)在每个子类中有所改变, 就使用getclass检测: if(getclass() != otherobject.getclass()) return false;

② 如果所有的子类都拥有统一的语义(比较的内容不变), 就使用instanceof检测: if(!(otherobject instanceof classname)) return false;

(6) 对所有需要比较的域进行比较: 使用==比较基本类型域, 使用equals比较对象域. 如果所有的域都匹配, 就返回true, 否则就返回flase:

① 如果在子类中重新定义equals()方法, 就要在其中包含调用super.equals(other);

② 当此方法被重写时, 通常有必要重写 hashcode() 方法, 以维护 hashcode 方法的常规协定, 该协定声明 相等对象必须具有相等的哈希码


参考资料

重写equal()时为什么也得重写hashcode()之深度解读equal方法与hashcode方法渊源

版权声明

作者:

出处: 博客园

感谢阅读, 如果文章有帮助或启发到你, 点个[