Java 中的 ==、equals 和 hashCode 你真的分的清吗?

==

在 Java 中,对于不同的数据类型,== 有不同的规则:

  1. 对于 byte、short、char、int、long、float、double、boolean 这些基本数据类型的比较,== 判断的是它们的值是否相等。
  2. 而如果出现了基本类型的包装类,比如用一个基本类型(int)和一个包装类(Integer)对象进行比较,使用 == 也能正确判断,因为在 Integer 和 int 进行比较时,会进行自动拆箱,这时也是判断它们的值是否相等。
  3. 而对于引用类型来说,== 比较的是它们的内存地址是否相等。
    int a = 1;
    int b = 1;
    byte c = 1;
    Integer d1 = new Integer(1); 
    Integer d2 = new Integer(1); 
    System.out.println(a == b); // true
    System.out.println(a == c); // true
    System.out.println(a == d1); // true
    System.out.println(d2 == a); // true
    System.out.println(d1 == d2); // false

还有一个比较有意思的现象是,当我们对包装类的 Integer 直接赋值的时候,d3 和 d4 返回的是 true,而 d5 和 d6 却返回的是 false。

    Integer d3 = 1;
    Integer d4 = 1;
    Integer d5 = 128;
    Integer d6 = 128;
    System.out.println(d3 == d4); // true
    System.out.println(d5 == d6); // false

这是什么原因呢?

实际上,Java 中存在一个 Integer 的常量池,-128 ~ 127 直接被缓存到常量池里。但是,对于 new 出来的 Integer 对象不适用于常量池。(感兴趣的小伙伴可以了解一下享元模式~)

那对于 String 类型又是怎么比较的呢?

    String e = "abc";
    String f = "abc";
    String g = new String("abc");
    String h = new String("abc");
    System.out.println(e == f); // true
    System.out.println(e == g); // false
    System.out.println(g == h); // false

对于普通的字符串变量,使用 == 判断,是可以返回正确结果的。

而对于 String 类来说,因为没有自动拆箱功能,所以,普通字符串变量和 new 出来的 String 对象进行比较时,返回的是 false。

此外,两个 new 出来的字符串对象在使用 == 比较时,返回的也是 false。

那么有了 == 进行比较,我们为什么还需要 equals 呢?

equals

实际上,如果我们不重写 equals 方法,Object 类中的 equals 和 == 其实是等效的。我们可以通过源码看出来:

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

而对象重写 equals 方法,更多的是追求两个对象在逻辑上的相等,判定相等的条件我们可以自定义。

比如,我们有一个 Person 类,其中有 id、name、age 等多个属性。如果我们的判定规则是通过判断 id 是否相等来比较两个 Person 对象,那么这个时候我们就可以重写 equals 方法。

    @Override
    public boolean equals(Object o) {
        if (o instanceof Person) {
            Person person = (Person) o;
            return Objects.equals(this.id, person.id);
        }
        return false;
    }

我们也可以看看 String.java 中是如何重写 equals 方法的:

    public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

 那说完了 equals,hashCode 又有什么用呢?

hashCode

作用

为了理解 hashCode 的作用,我们首先要知道,在 Java 中的集合有两类,一类是 List,还有一类是 Set。

前者集合内的元素是有序且可以重复的,而后者是无序不可以重复的。

equals 方法可用于保证元素不重复,但如果每增加一个元素就检查一次,就会大大降低效率。因此,Java 采用了哈希表的原理:

当集合要添加新的元素的时候,先调用这个元素的 hashCode 方法,这样就可以一下子定位到它应该放置的物理位置。

  • 如果得到的位置上没有元素,那么就可以直接把元素存储在这个位置上,不用再进行任何比较;
  • 如果这个位置上已经有元素了,就调用它的 equals 方法与新元素进行比较,相同的话就不再进行存储了;
  • 如果不相同,那么就发生了哈希冲突,这时候就会在这个位置上生成一个链表,将所有产生相同 hashCode 的对象存放在这个链表上;

因此,我们可以看出,实际调用 equals 方法的次数降低了,hashCode 大大解决了插入元素效率低下的问题。

特性

  • 首先,两个不同对象 x、y,x.hashCode() 和 y.hashCode() 基本上不会相同;
  • 字符串的散列码是由内容导出的,Object 类的默认 hashCode 会从对象的存储地址得出散列码;
  • Objects.hash(Object... values) 返回一个散列码,是由提供的所有对象的散列码组合而得到的。
  • equals 与 hashCode 的定义必须相容:如果 x.equals(y) 返回 true,那么 x.hashCode() 就必须与 y.hashCode() 返回相同的值;
  • 两个对象 equals 相等,那么它们的 hashCode 一定也相等;
  • 两个对象的 hashCode 相等,但它们 equals 不一定相等;
  • 通常只要我们重写 equals 方法,就要重写 hashCode 方法;

参考文章

java中==,equals,hashcode - 简书

Objects.equals有坑

Java基础篇:hashCode的作用_M_Jack的博客-CSDN博客_javahashcode作用

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值