首页 > 编程语言 > 详细

Java hashCode()和equals()的相关问题

时间:2021-03-30 20:53:53      阅读:26      评论:0      收藏:0      [点我收藏+]

1. ==和equals的区别

1.1 ==

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

因为 Java 只有值传递,所以,对于 == 来说,不管是比较基本数据类型,还是引用数据类型的变量,其本质比较的都是值,只是引用类型变量存的值是对象的地址。

1.2 equals()

它的作用也是判断两个对象是否相等,它不能用于比较基本数据类型的变量。equals()方法存在于Object类中,而Object类是所有类的直接或间接父类。

Objectequals()方法:

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

equals() 方法存在两种使用情况:

  • 情况 1:类没有覆盖 equals()方法。则通过equals()比较该类的两个对象时,等价于通过“==”比较这两个对象。使用的默认是 Objectequals()方法。
  • 情况 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为另一个引用,对象的内容一样
        if (a == b) // false,非同一对象
            System.out.println("a==b");
        if (a.equals(b)) // true
            System.out.println("aEQb");
    }
}

说明:

  • String 中的 equals 方法是被重写过的,因为 Objectequals 方法是比较的对象的内存地址,而 Stringequals 方法比较的是对象的值。

2 hashCode()和equals()

1 hashCode()介绍:

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

public native int hashCode();

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

2 为什么要有 hashCode?

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

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

3 为什么重写 equals 时必须重写 hashCode 方法?

如果两个对象相等,则 hashcode 一定也是相同的。两个对象相等,对两个对象分别调用 equals 方法都返回 true。但是,两个对象有相同的 hashcode 值,它们也不一定是相等的 。因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖。

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

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

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

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

3 重写equlas()和hashCode()

class Person{
     private int age;
     private String name;


    public Person(int age, String name) {
        this.age = age;
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(age, name);
    }

    // getter and setter...
}
  1. 重写equals()

    • 比较当前对象和比较对象是否是同一个对象,若是则直接返回true
    • 判断比较对象是否为空,或者当前对象和比较对象的class对象是否不同,若是则返回false
    • 将比较对象向下转型
    • 判断当前对象和比较对象的内容是否相同
  2. 重写hashCode()

    • 调用Objects类中的hash方法,然后调用Arrays类中的hashCode方法

    • 具体实现

      //Objects.java
      public static int hash(Object... values) {
            return Arrays.hashCode(values);
      }
      
      //Arrays.java
      public static int hashCode(Object a[]) {
          if (a == null)
              return 0;
      
          int result = 1;
      
          for (Object element : a)
             result = 31 * result + (element == null ? 0 : element.hashCode());
      
          return result;
      }
      

4 重写hashCode()可能造成的内存泄漏问题

代码如下所示,其中的Person类为在上面已经定义,并且重写了equals()hashCode()方法

public static void main(String[] args) throws IllegalAccessException, InstantiationException {
    Person p1 = new Person(20,"p1");
    Set<Person> set = new HashSet<>();
    set.add(p1);
    System.out.println("before remove: "+set.size());
    p1.setAge(5);
    set.remove(p1);
    System.out.println("after remove: "+set.size());
}

运行结果:

before remove: 1
after remove: 1

这里发现一个问题,将对象的属性值更改后,再调用集合的remove()方法无法删除掉相应的对象

原因是在调用remove()方法时会调用对象的hashCode()方法来找这个对象(可查看remove()方法的源码),如果对象的属性值改变,再次调用hashCode()方法时其返回值就会发生改变,因此会找不到该对象所在的位置,自然无法删除该对象。

上面的这个内存泄露告诉我们一个信息:如果对象的属性值参与了hashCode的运算,将对象放进散列集合后,如果后续要进行删除,就不能对其属性值进行修改,否则会出现严重的问题(找不到该对象)。

同理在其它需要调用hashCode方法的地方也不能修改对象的属性值,如contains()方法等,也会出现找不到对象的问题

Java hashCode()和equals()的相关问题

原文:https://www.cnblogs.com/Wenjin-Liu/p/14597462.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!