equels、hashCode

1、==与eques

== : 它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不是同一个对象。(基本数据类型==比较的是值,引用数据类型==比较的是内存地址)

equals() : 它的作用也是判断两个对象是否相等。但它一般有两种使用情况:

情况1:类没有覆盖 equals() 方法。则通过 equals() 比较该类的两个对象时,等价于通过“==”比较这两个对象。

情况2:类覆盖了 equals() 方法。一般,我们都覆盖 equals() 方法来两个对象的内容相等;若它们的内容相等,则返回 true (即,认为这两个对象相等)。

复制代码
public class test1 {
  public static void main(String[] args) {
    String a = new String("ab"); // a 为一个引用
    String b = new String("ab"); // b为另一个引用,对象的内容一样
    String aa = "ab"; // 放在常量池中
    String bb = "ab"; // 从常量池中查找
    if (aa == bb) // true
      System.out.println("aa==bb");
    if (a == b) // false,非同一对象
      System.out.println("a==b");
    if (a.equals(b)) // true
      System.out.println("aEQb");
    if (42 == 42.0) { // true
      System.out.println("true");
   }
 }
}
复制代码
  • String 中的 equals 方法是被重写过的,因为 object 的 equals 方法是比较的对象的内存地址,而 String 的equals 方法比较的是对象的值。
  • 当创建 String 类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个 String 对象。

 2、hashCode与equals?为什么重写equals时必须重写hashCode方法?”

(1)hashCode()介绍
  hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode() 定义在JDK的Object.java中,这就意味着Java中的任何类都包含有hashCode() 函数。另外需要注意的是: Object 的 hashcode 方法是本地方法,也就是用 c 语言或 c++ 实现的,该方法通常用来将对象的 内存地址 转换为整数之后返回。

散列表存储的是键值对(key-value),它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利用到了散列码!(可以快速找到所需要的对象)

(2)为什么要有hashCode

  我们以“HashSet如何检查重复”为例子来说明为什么要有hashCode:

  当你把对象加入HashSet时,HashSet会先计算对象的hashcode值来判断对象加入的位置,同时也会与其他已经加入的对象的hashcode值作比较,如果没有相符的hashcode,HashSet会假设对象没有重复出现。但是如果发现有相同hashcode值的对象,这时会调用equals()方法来检查hashcode相等的对象是否真的相同。如果两者相同,HashSet就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。(摘自我的Java启蒙书《Head fistjava》第二版)。这样我们就大大减少了equals的次数,相应就大大提高了执行速度。

(3)hashCode()与equals()的相关规定

1. 如果两个对象相等,则hashcode一定也是相同的

2. 两个对象相等,对两个对象分别调用equals方法都返回true

3. 两个对象有相同的hashcode值,它们也不一定是相等的

4. 因此,equals方法被覆盖过,则hashCode方法也必须被覆盖

5. hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写hashCode(),则该class的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)

(4) 为什么两个对象有相同的hashcode值,它们也不一定是相等的?

  在这里解释一位小伙伴的问题。以下内容摘自《Head Fisrt Java》。

  因为hashCode() 所使用的杂凑算法也许刚好会让多个对象传回相同的杂凑值。越糟糕的杂凑算法越容易碰撞,但这也与数据值域分布的特性有关(所谓碰撞也就是指的是不同的对象得到相同的 hashCode)。

  我们刚刚也提到了 HashSet,如果 HashSet 在对比的时候,同样的 hashcode 有多个对象,它会使用 equals() 来判断是否真的相同。也就是说 hashcode 只是用来缩小查找成本。


3、为什么重写equals时必须重写hashcode?

一、直接给出答案

  因为Hash比equals方法的开销要小,速度更快,所以在涉及到hashcode的容器中(比如HashSet),判断自己是否持有该对象时,会先检查hashCode是否相等,如果hashCode不相等,就会直接认为不相等,并存入容器中,不会再调用equals进行比较。

  这样就会导致,即使该对象已经存在HashSet中,但是因为hashCode不同,还会再次被存入。

  因此要重写hashCode保证:如果equals判断是相等的,那hashCode值也要相等

二、代码测试

  • 编写一个只重写equals的实体类,不重写hashCode进行测试

    我们希望只要id是一样的,就认定为是一个对象,集合中同一个对象只存一个

复制代码
package com.yang.servlet;


import java.util.HashSet;
import java.util.Objects;

public class Product {
    private Integer id;
    private String name;

    public Product(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    // 重写equals
    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        Product product = (Product) o;
        return Objects.equals(id, product.id);
    }

    
    public static void main(String[] args) {
        Product product1 = new Product(1, "包子");
        Product product2 = new Product(1, "馒头");

        HashSet<Product> products = new HashSet<Product>();
        products.add(product1);
        products.add(product2);
        // 使用equals判断是否相等
        System.out.println(product1.equals(product2));
        // 查看HashSet中元素个数
        System.out.println(products.size());

    }
}
复制代码

【测试结果】
true // 可以看到判断是相等的
2 // 但是还是存到了HashSet中

  • 重写hashCode,再次测试
@Override
public int hashCode() {
    return Objects.hash(id);
}

【测试结果】

true // 可以看到判断是相等的

1 // 并且第二个值并没有存到HashSet中





posted @   guoyu1  阅读(560)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示