2018开年第一篇:equals()与hashCode()

100 篇文章 1 订阅
97 篇文章 1 订阅

equals()hashcode()java.lang.Object中提供的用以对象比较的两个重要方法,下面是其定义及默认实现:

  • public boolean equals(Object obj) { return (this == obj); }:用以判断变量参数与当前实例是否相等,JDK默认实现是基于对象内存地址是否相同,如果两个对象内存地址相同,则表示两个对象相同。
  • public native int hashCode();: 默认情况下,该方法返回一个随机整数,对于每个对象来说该数字唯一,但该数字并非恒定,可能随着程序的执行发生变化。

equals() 与 hashCode() 使用契约

If two objects are equal according to the equals(Object) method, then calling the hashcode() method on each of the two objects must produce the same integer result

上面的话翻译成程序语言即是:

if obj1.equal(obj2) then:
    obj1.hashCode() == obj2.hashCode()

在实际应用当中,JDK提供的默认实现可能无法满足实际业务场景,这时,我们就需要根据业务场景来重载hashCodeequals方法,但需谨记:当我们重载一个对象的equals方法,就必须重载他的hashCode方法,如果我们仅仅重载equals但没有重载hashcode,实际应用可能会带来潜在问题,接下我们示例说明:

示例说明

定义Student类

public class Student {
    private int id;
    private String name;
    public Student(int id, String name) {
        this.name = name;
        this.id = id;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

测试主类HashcodeEquals,测试两个Student类实例是否相同

public class HashcodeEquals {
    public static void main(String[] args) {
        Student alex1 = new Student(1, "Alex");
        Student alex2 = new Student(1, "Alex");
        System.out.println("alex1 hashcode = " + alex1.hashCode());
        System.out.println("alex2 hashcode = " + alex2.hashCode());
        System.out.println("Checking equality between alex1 and alex2 = " + alex1.equals(alex2));
    }
}

输出如下:

alex1 hashcode = 1852704110
alex2 hashcode = 2032578917
Checking equality between alex1 and alex2 = false

上面的输出也很好理解:尽管两个实例属性完全相同,但根据JDK中equals的默认实现规则,他们的内存地址不同,因此,在equals的默认实现中,它们被认为是不相等的,这同样适用于hashCode —— JDK为每个实例生成一个随机的惟一哈希值

重载equals()方法

实际业务场景中,如果两个student实例ID相同,那么我们即可认为实例相同,因此,我们重载equals方法,重载规则如下:内存地址一致或者实例ID相同,代码如下:

@Override
public boolean equals(Object obj) {
    if (obj == null) return false;
    if (!(obj instanceof Student))
        return false;
    if (obj == this)
        return true;
    return this.getId() == ((Student) obj).getId();
}

重新执行判断语句,结果如下

alex1 hashcode = 2032578917
alex2 hashcode = 1531485190
Checking equality between alex1 and alex2 = true

ArrayList中使用equals()方法

equals方法另一个常用业务是从一组实例中查找指定实例是否存在:

public class HashcodeEquals {
    public static void main(String[] args) {
        Student alex = new Student(1, "Alex");
        List < Student > studentsLst = new ArrayList < Student > ();
        studentsLst.add(alex);
        System.out.println("Arraylist size = " + studentsLst.size());
        System.out.println("Arraylist contains Alex = " + studentsLst.contains(new Student(1, "Alex")));
    }
}

执行结果如下:

Arraylist size = 1
Arraylist contains Alex = true

重载hashCode()方法

通过上面的实例我们可以看到,通过重载equals方法,我们得到了我们期待的结果,即使是两个实例对象的哈希值并不相同,那么问题来了?重载hashCode方法的目的何在?

HashSet中使用equals方法

我们用一个例子来说明这个问题:


public class HashcodeEquals {
    public static void main(String[] args) {
        Student alex1 = new Student(1, "Alex");
        Student alex2 = new Student(1, "Alex");
        HashSet < Student > students = new HashSet < Student > ();
        students.add(alex1);
        students.add(alex2);
        System.out.println("HashSet size = " + students.size());
        System.out.println("HashSet contains Alex = " + students.contains(new Student(1, "Alex")));
    }
}

运行上面的代码,我们得到如下输出:

HashSet size = 2
HashSet contains Alex = false

上述例子中,我们应该已经看出了问题,尽管我们已经重载了equals方法,理论上 alex1与 alex1应该是同一个对象,而HashSet是无法存储重复对象,但为什么JVM会认为他们是不同的对象。

这涉及到了HashSet的内部存储结构,在JDK中,HashSet将其内部元素存储在内存桶中。每个内存桶都关联一个特定的哈希值。在调用student.add(alex1)时,Java将alex1存储在一个内存桶中,并将其与alex1.hashCode()的值关联。后续,一旦插入相同哈希值的对象,那么插入对象将替换原有对象alex1。但是,由于alex2与alex1哈希值不同,因此被JVM视为完全不同的对象,将其存储在另外一个单独的内存桶中。

这就是重载hashCode的重要性,因此,我们按照业务场景重载hashCode方法,确保拥有相同ID的Student实例存储在同一个内存桶中,代码如下:

@Override
public int hashCode() {
    return id;
}

之后,我们重写运行测试代码,执行结果如下:

HashSet size = 1
HashSet contains Alex = true

这就是hashCode的魅力所在,这两个实例被认为是一个对象并存储在同一个内存桶中,后续通过contains方法查找对象时,只要哈希值相同,对象即可被查到,这样适用于使用哈希机制存储对象的数据结构,如:HashMap, HashTable

结论

  • 如果两个对象相同,则他们的哈希值(hashcode)一定相同

  • 如果两个对象的哈希值相同(hashcode)相同,并不意味着他们是相同的。

  • 对于使用Hash散列方式存储对象的数据结构:HashSet、HashMap、HashTable等,仅仅重载equals方法可能会导致实际业务逻辑失败

  • 在比较两个对象时,仅重载hashCode方法并不能强制Java忽略内存地址。

https://dzone.com/articles/working-with-hashcode-and-equals-in-java

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
项目:– JavaScript 中的患者数据管理系统 患者数据管理系统是为医院开发的 node JS 项目。通过使用此系统,您可以轻松访问患者数据,它具有成本效益,可改善患者护理和数据安全性。不仅如此,它还减少了错误范围。在运行项目之前,您需要下载 node.js。 这个患者数据管理项目包含 javascript、node.js 和 CSS。我们必须让服务器监听端口 3000,并使用 JSON 在客户端和服务器之间交换数据。这个项目会不断询问您有关插件更新的信息,因此请保持互联网畅通。此系统允许您执行 crud 操作。在这里,您是系统的管理员。您还可以添加所需的员工人数。此外,您还可以更新患者记录。该系统功能齐全且功能齐全。 要运行此项目,您需要在计算机上安装NodeJS并使用现代浏览器,例如 Google Chrome、  Mozilla Firefox。ReactJS项目中的此项目可免费下载源代码。有关项目演示,请查看下面的图像滑块。 对于手动安装 1.将主项目文件夹解压到任意目录 2.从 cmd 设置项目目录的路径 3. 输入命令“npm install” 4.完成后输入命令“npm start” 5.现在,您将获得一个 localhost:portnumber,并转到该 URL 演示: 该项目为国外大神项目,可以作为毕业设计的项目,也可以作为大作业项目,不用担心代码重复,设计重复等,如果需要对项目进行修改,需要具备一定基础知识。 注意:如果装有360等杀毒软件,可能会出现误报的情况,源码本身并无病毒,使用源码时可以关闭360,或者添加信任。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值