Java equals()和hashCode()

介绍:

Java Object类提供了方法的基本实现– hashCode()equals()。 这些方法非常有用,尤其是在使用Collection框架时。 哈希表实现依赖于这些方法来存储和检索数据。

在本教程中,我们将学习hashCode()equals()之间的协定它们的默认实现)。 我们还将讨论何时以及如何覆盖这些方法。

默认行为:

首先让我们看一下这些方法的默认实现:

存在于Object类中的equals()方法只是比较对象引用:

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

因此,默认情况下, obj1.equals(obj2)obj1 == obj2相同。

equals()方法比较诸如String等的类的实际值,因为它们在相应的类中被覆盖。

JDK中hashCode()方法的签名为:

public native int hashCode();

在这里, native关键字表示该方法是使用JNI (Java本机接口)以本机代码实现的。

hashCode()方法返回一个int类型。 默认情况下,返回值表示对象存储器地址。

实施原则:

在覆盖equals()hashCode()方法之前,我们先来看一下准则:

1. equals():   我们对equals()方法的实现必须是:

  • 反身:对于任何参考值objobj.equals(obj)应该返回true
  • 对称:对于参考值obj1obj2 ,如果obj1.equals(obj2)true,obj2.equals(obj2)也应返回true
  • 传递性:对于值OBJ1参考,OBJ 2和 OBJ 3,如果obj1.equals(OBJ 2) 真实 ,obj2.equals(OBJ 3) ,那么obj1.equals(OBJ 3)也应该返回true
  • 一致:只要我们没有更改实现, equals()方法的多次调用必须始终返回相同的值

2. hashCode():实现hashCode()时,必须考虑以下几点:

  • 在一次执行中, hashCode()的多次调用必须返回相同的值,前提是我们不更改equals()实现中的属性
  • 相等的对象必须返回相同的hashCode()
  • 两个或更多不相等的对象可以具有相同的hashCode()

尽管在覆盖这些方法时要牢记上述所有原则,但是其中有一个流行的规则:

对于两个对象obj1obj2

  • 如果obj1.equals(obj2),obj1.hashCode()= obj2.hashCode()必须为true
  • 但是,如果obj1.hashCode()== obj2.hashCode() ,则obj1.equals(obj2)可以返回truefalse,即obj1obj2可能相等或不相等

这通常被称为equals()hashCode()契约。

为什么覆盖

hashCode()equals()方法在基于哈希表的实现中存储和检索元素方面起着重要作用。 hashCode()确定给定项映射到的存储桶。 在存储桶中, equals()方法用于查找给定的条目。

假设我们有一个Employee类:

public class Employee {
 
    private int id;
    private String name;
 
    //constructors, getters, setters, toString implementations
 
}

还有一个存储Employee作为键的HashMap

Map<Employee, Integer> map = new HashMap<>();
 
map.put(new Employee(1, "Sam"), 1);
map.put(new Employee(2, "Sierra"), 2);

现在我们已经插入了两个条目,让我们尝试一个containsKey()检查:

boolean containsSam = map.containsKey(new Employee(1, "Sam")); //false

尽管我们有Sam的条目,但是containsKey()返回false 。 这是因为我们尚未覆盖equals()hashCode()方法。 默认情况下, equals()只会进行基于引用的比较。

覆盖

根据Javadocs:

当我们覆盖equals()方法时,我们还必须覆盖hashCode()方法。

这将有助于避免违反equals-hashCode合同。

请注意,如果我们违反合同,编译器不会抱怨,但是当我们将此类对象作为键存储在HashMap中时,最终可能会遇到意外行为。

我们可以使用IDE的功能快速覆盖这些方法。 使用Eclipse时,我们可以转到Source-> Generate hashCode()和equals()。 让我们看看为Employee类生成的实现:

public class Employee {
 
    ...
     
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + id;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }
 
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Employee other = (Employee) obj;
        if (id != other.id)
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } 
        else if(!name.equals(other.name))
            return false;
 
        return true;
    }
}

显然,相同的字段已用于实现equals()hashCode()方法,以与合同保持一致。

最佳做法:

使用equals()hashCode()时应遵循的一些最佳实践包括:

  • 实现hashCode()以在各个存储桶之间平均分配项目。 这样做的目的是最大程度地减少碰撞次数,从而获得良好的性能
  • 对于equals()hashCode()实现,我们应该使用相同的字段
  • HashMap中将不可变对象作为键,因为它们支持缓存哈希码值
  • 使用ORM工具时,请始终使用getter代替hashCode()equals()方法定义中的字段。 那是因为有些字段可能是延迟加载的

结论:

在本教程中,我们首先研究了equals()hashCode()方法的默认实现。 稍后,我们讨论了何时以及如何覆盖这些方法。

成为第一个发表评论的人。

翻译自: https://www.javacodegeeks.com/2019/05/java-equals-hashcode.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值