equals使用及重写equals方法为什么要重写hashCode?

一. equals的使用

用来判断两个对象是否相等。

  • 类如果没有重写Object的equals方法,则调用Object的equals方法,比较的是对象在堆中的地址是否相等
  • 类如果重写了equals方法,一般用来比较对象的属性是否相等
二. 为什么要重写equals?

为什么要重写equals方法?主要看使用场景,如果不需要进行对象比较就不需要重写。
举个栗子,用户要修改用户名,传了个User对象过来,此时从DB里取出旧的User对象,比较两个是不是相等的(其实是比较用户名),如果没有重写equals方法,用的是Object的equals方法,比较对象的地址,与我们的意愿相违背。

例子1

public class Main {
    
    
    public static void main(String[] args) {
    
    
        User newUser = new User("dkangel");
        User dbUser = new User("dkangel");
        System.out.println(newUser.equals(dbUser));
    }
}

1. 未重写equals方法,结果为false

public class User {
    
    
    private String name;
    
    public User(String name) {
    
    
        this.name = name;
    }
    
    public String getName() {
    
    
        return name;
    }

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

2. 重写equals方法,结果为true

public class User {
    
    
    private String name;

    public User(String name) {
    
    
        this.name = name;
    }

    public String getName() {
    
    
        return name;
    }

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

    /**
     * 重写equals方法,比较name属性是否相等
     *
     * @param obj 待比较对象
     * @return boolean
     */
    @Override
    public boolean equals(Object obj) {
    
    
        if (this == obj) {
    
    
            return true;
        }
        if (!(obj instanceof User)) {
    
    
            return false;
        }
        User user = (User) obj;
        return Objects.equals(user.name, this.getName());
    }
}
三. 重写了equals方法一定要重写hashCode方法吗?为什么?

hashCode方法的作用是获取哈希码(也称散列码),哈希码的作用是确定对象在哈希表中的位置。

先看下 hashCode 与 equals 的相关规定

  • 如果两个对象相等,则 hashcode 一定也是相同的
  • 两个对象相等,对两个对象分别调用 equals 方法都返回 true
  • 两个对象有相同的 hashcode 值,它们也不一定是相等的
  • hashCode方法的默认行为是对堆上的对象产生独特值。如果没有重写 hashCode方法,则该类的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)

从约定上看:重写equals方法必须重写hashCode方法。

从使用上看:

  • 如果类在使用过程中不需要创建类对应的哈希表(就是不会在HashMap/HashSet/HashTable等用到hash的数据数据里用到该类),那就与hashCode方法没什么关系,也就不需要重写hashCode方法。如上面的例子1,只重写了equals方法,没有重写hashCode方法,并不影响使用。

  • 如果类在使用过程中需要创建类对应的哈希表,那就需要重写hashCode方法,为什么?再举个栗子

例子2

当你把对象加入 HashSet 时,HashSet 会先计算对象的 hashcode 值来判断对象加入的位置,同时也会与该位置其他已经加入的对象的 hashcode 值作比较,如果没有相符的 hashcode,HashSet 会假设对象没有重复出现。但是如果发现有相同 hashcode 值的对象,这时会调用 equals()方法来检查 hashcode 相等的对象是否真的相同。如果两者相同,HashSet 就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。

public class Main {
    
    
    public static void main(String[] args) {
    
    
        User newUser = new User("dkangel");
        User dbUser = new User("dkangel");
        User otherUser = new User("zhangsan");

        HashSet<User> users = new HashSet();
        users.add(newUser);
        users.add(dbUser);
        users.add(otherUser);

        System.out.println("users size: " + users.size());
        users.forEach(user -> System.out.printf("name: %s, hashCode: %s\n", user.getName(), user.hashCode()));
    }
}

1. 未重写hashCode方法
结果为3,两个name为“dkangel”的对象由于未重写hashCode方法,导致set中出现重复值。
在这里插入图片描述
2. 重写hashCode方法

public class User {
    
    
    private String name;

    public User(String name) {
    
    
        this.name = name;
    }

    public String getName() {
    
    
        return name;
    }

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

    @Override
    public boolean equals(Object obj) {
    
    
        if (this == obj) {
    
    
            return true;
        }
        if (!(obj instanceof User)) {
    
    
            return false;
        }
        User user = (User) obj;
        return Objects.equals(user.name, this.getName());
    }

    @Override
    public int hashCode() {
    
    
        return this.name.hashCode();
    }
}

结果为2,重写了hashCode方法两个name为“dkangel”的对象只添加了一个到set里。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/Dkangel/article/details/105980473