'=='&equals&hashCode的深入理解

本文详细解析Java中基本数据类型与引用数据类型的差异,深入探讨==与equals方法的区别及应用场景,同时讲解如何正确重写equals与hashCode方法,确保对象一致性的同时提升集合操作效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. Java数据类型
  • 基本数据类型:基本类型数据不存在“引用”概念,其值直接存储在内存中的内存栈的栈空间里面;
  • 引用数据类型:简单来说,“引用”是存储在内存的内存栈上,对象本身存储在内存堆上。
  • 举个例子:

    Object o = new Object();
    o 是存在于栈中指向堆里面的实例化对象 Object。此时 o 为 null,代表 o 指向为空,是没有地址存在的。但是 Object o = “”,o是有地址的,只是地址代表的内容为空。

2. == 与 equals() 的区别
  • 对于基本数据类型之间的比较,应用“==”进行比较,比较的是他们的值;

  • 对于引用数据类型,用“”进行比较,比较的是他们在内存中的存放地址。Java所有的类都是继承于Object基类,在Object中的基类中定义了一个equals()方法,这个方法的初始行为就是比较对象的内存地址。因此,对于没有重写 equals() 方法的类,他们之间还是比较的是内存地址的值,“”和equals() 是等价的。但我们常用的String、Integer、Date等这些类重写了equals() 方法,他们就不是比较内存地址了。案例如下:

    String str1 = "ab";
    String str2 = "cd";
    String str3 = "abcd";
    String str4 = "abcd";
    String str5 = new String("abcd");
    String str6 = new String("abcd");
    
    boolean b1 = str1 == str2; //false
    boolean b2 = (str1 + str2) == str3; //false
    boolean b3 = str4 == str3; //true
    boolean b4 = str5 == str6; //false
    boolean b5 = (str1 + str2).equals(str4); //true
    boolean b6 = str5.equals(str6); //true
    
3. 重写对象的 equals 方法

我们看下面这段代码

public class EqualsTest {
    public static void main(String[] args) {
        Cat cat1 = new Cat("red", "10", 2);
        Cat cat2 = new Cat("red", "10", 2);
        System.out.println(cat1 == cat2); //false
        System.out.println(cat1.equals(cat2)); //false
    }
}

class Cat {
    private String color;
    private String height;
    private int age;
    
    public Cat() {}
    public Cat(String color, String height, int age) {
        this.color = color;
        this.height = height;
        this.age = age;
    }
}

从前面我们所知道的“==”和 equals() 的定义,我们打印出来的结果都得为 false,事实也是如此。如果我们定义两只 Cat 的 color、height 和 age 相等时,那这两只 Cat 就是同一只。那么我们只能通过重写 Cat 类的 equals 方法来实现我们的需求。

@Override
public boolean equals(Object object){
    if (object == null) {
        return false;
    }
    if (object instanceof Cat) {
        Cat c = (Cat)object;
        if (c.getColor().equals(this.getColor()) && c.getHeight().equals(this.getHeight())&& c.getAge() == this.getAge()) {
            return true;
        }
    }
    return false;
}
4. equals 和 HashCode 详解

问题引入:我们都知道 Collection 下有个 Set 集合,它的元素是无序的且不可重复。当我们往里面存数据的时候,它是怎么保证元素不重复?或者说是依据什么来判断?如果我们使用 equals() 方法(被重写过),每增加一个元素就检查一次,当里面有成百上千个元素时,我们每增加一个元素就需要调用成百上千次 equals() 方法,这显然是不可取的,效率太低。

因此有人发明了一种哈希算法来提高从集合中查找元素的效率,这种方式将集合分成若干个存储区域,每个对象可以算出一个哈希码,将哈希码分组并分别对应到储存区域,因此根据一个对象的哈希码就可以确定对象储存在哪个区域(我的理解是就像在一根绳子上挂灯笼,比如有5个节点可以挂灯笼,根据不同的灯笼样式挂在不同的节点,如果两个灯笼样式一致,但是却不是同一个,我们就把这个灯笼挂在上一个的下面)。因此简单的来说,当我们往 Set 集合中储存元素时,会先根据元素的 hashCode()方法获取元素的哈希码,然后找到对应储存区域,并通过 equals 方法与该区域内的其他元素进行比较;这样就可以不遍历集合中所有的元素来判重,进而提高效率。

那么问题来了,如果我们重写了对象的 equals() 方法,而不重写的它的 hashCode() 方法,那我们上述论述毫无意义;因为 Object 类中的 hashCode() 方法始终返回的是一个对象的 hash 地址,而这个地址是永远不相等的,即使重写了 equals() 方法,但一开始 hashCode() 方法返回的哈希值都不在一个区域的话那么就不会去调用 equals() 方法了。

上述我们就能得出一个结论:

equals() 相等的两个对象,hashCode() 一定相等;equals() 不相等的两个对象,hashCode() 有可能相等。

5. hashCode 使用注意事项

首先我们看个例子,我们重写了测试类的 equals() 和 hashCode() 方法,get 和 set 方法并没有贴上来:

class ObjectTest {
    private int x;
    private int y;

    @Override
    public boolean equals(Object object) {
        if (object == null) {
        return false;
    }
    if (object instanceof ObjectTest) {
        ObjectTest t = (ObjectTest)object;
            if (t.getX() == this.getX() && t.getY() == this.getY()) {
            return true;
            }
        }
        return false;
    }
    
    @Override
    public int hashCode() {
        final int temp = 11;
        int result = 1;
        result = temp * result + x;
        result = temp * result + y;
        return result;
    }
}

测试:

public static void main(String[] args) {
    HashSet<ObjectTest> set = new HashSet<>();
    ObjectTest o1 = new ObjectTest(2, 2);
    ObjectTest o2 = new ObjectTest(3, 3);
    ObjectTest o3 = new ObjectTest(2, 2);

    set.add(o1);
    set.add(o2);
    set.add(o3);
    set.add(o1);
    System.out.println(set.size());
}

打印结果为:2。这个很好理解,存入了o1、o2;
当我们把 hashCode 的方法注释掉,打印结果为:3。因为Object中的hashCode方法返回的是对象本地内存地址的换算结果,o1与o3虽然equals相同,但是hashCode不同,就不会去验证equals的方法;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值