SpringBoot 重写hashCode方法和equals方法

Posted 张志翔 ̮

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot 重写hashCode方法和equals方法相关的知识,希望对你有一定的参考价值。

一、不重写hashCode方法和equals方法

1. Phone对象

代码如下(示例):

public class Phone 
    private String name;
    private Integer price;

    public Phone(String name, Integer price) 
        this.name = name;
        this.price = price;
    

    public String getName() 
        return name;
    

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

    public Integer getPrice() 
        return price;
    

    public void setPrice(Integer price) 
        this.price = price;
    

    @Override
    public String toString() 
        return "Phone" +
                "name='" + name + '\\'' +
                ", price=" + price +
                '';
    

2. 将Phone 对象添加入set

代码如下(示例):

public static void main(String[] args) 
        HashSet hashSet = new HashSet();
        hashSet.add(new Phone("华为", 6666));
        hashSet.add(new Phone("小米", 1999));
        hashSet.add(new Phone("华为", 6666));
        
        Iterator iterator = hashSet.iterator();
        while (iterator.hasNext()) 
            System.out.println(iterator.next().toString());
        
        
    

3. 运行结果

 因为没有重写hashCode方法和equals方法,导致数据重复添加

如果对象未重写 hashCode方法,则会使用父类Object的hashCode,Object的hashCode每次new一个对象就会有不同的hash值,如果不重写,即使相同的对象也能被添加。

二、重写hashCode方法和equals方法

1. Phone对象

代码如下(示例):

public class Phone 
    private String name;
    private Integer price;

    public Phone(String name, Integer price) 
        this.name = name;
        this.price = price;
    

    public String getName() 
        return name;
    

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

    public Integer getPrice() 
        return price;
    

    public void setPrice(Integer price) 
        this.price = price;
    

    @Override
    public String toString() 
        return "Phone" +
                "name='" + name + '\\'' +
                ", price=" + price +
                '';
    

    @Override
    public boolean equals(Object o) 
        if (this == o) return true;
        if (!(o instanceof Phone)) return false;
        Phone phone = (Phone) o;
        return getName().equals(phone.getName()) &&
                getPrice().equals(phone.getPrice());
    

    @Override
    public int hashCode() 
        return Objects.hash(getName(), getPrice());
    


2. 将Phone 对象添加入set

代码如下(示例):

public static void main(String[] args) 
        HashSet hashSet = new HashSet();
        hashSet.add(new Phone("华为", 6666));
        hashSet.add(new Phone("小米", 1999));
        hashSet.add(new Phone("华为", 6666));

        Iterator iterator = hashSet.iterator();
        while (iterator.hasNext()) 
            System.out.println(iterator.next().toString());
        
    

3. 运行结果

重写hashCode方法和equals方法后就解决了数据重复添加的问题。
思考:
重写后如果改变了对象的属性,那么计算出的hashCode还是原来的吗?
答案在下面
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

三、对象做为可变key

1. Phone对象

代码如下(示例):

public class Phone 
    private String serialNumber;
    private String name;
    private Integer price;

    public Phone(String serialNumber, String name, Integer price) 
        this.serialNumber = serialNumber;
        this.name = name;
        this.price = price;
    

    public String getSerialNumber() 
        return serialNumber;
    

    public void setSerialNumber(String serialNumber) 
        this.serialNumber = serialNumber;
    

    public String getName() 
        return name;
    

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

    public Integer getPrice() 
        return price;
    

    public void setPrice(Integer price) 
        this.price = price;
    


    @Override
    public String toString() 
        return "Phone" +
                "serialNumber='" + serialNumber + '\\'' +
                ", name='" + name + '\\'' +
                ", price=" + price +
                '';
    


    @Override
    public boolean equals(Object o) 
        if (this == o) return true;
        if (!(o instanceof Phone)) return false;
        Phone phone = (Phone) o;
        return getSerialNumber().equals(phone.getSerialNumber()) &&
                getName().equals(phone.getName()) &&
                getPrice().equals(phone.getPrice());
    

    @Override
    public int hashCode() 
        return Objects.hash(getSerialNumber(), getName(), getPrice());
    

2. 将Phone 对象作为key放入map

代码如下(示例):

public static void main(String[] args) 
        Map map = new HashMap();
        Phone phone = new Phone(UUID.randomUUID().toString(), "华为", 8888);

        /**
         * 会根据重写后的hashCode值,经过位移计算,找到对应的位置,插入到里面
         */
        map.put(phone, "val");
        System.out.println("第一次获取 :" + map.get(phone));
        /**
         * 当改变对象里的属性的时候,重写后的hashCode值也随之改变,
         * 当获取的时候已经不是上次所插入的位置了,所以获取的为null
         */
        phone.setName("小米");
        System.out.println("第二次获取 :" + map.get(phone));
    

3. 运行结果

由图可看出:
第一次获取时,是有值的,当改变其name属性在获取,就获取不到了。

这是因为当存储数据时会根据重写后的hashCode值,经过位移计算,找到对应的位置,插入到里面,当改变其name属性后,重写后的hashCode值也随之改变,获取的位置已经不是上次所插入的位置了,所以获取的为null

针对上面的问题,我们可以选择将不容易被改变的属性用来计算hashCode值

改造phone对象

public class Phone 
    private String serialNumber;
    private String name;
    private Integer price;

    /**
     * 只能根据编号serialNumber实例化对象,且serialNumber不可被更改,所以去掉其setSerialNumber方法
     * @param serialNumber
     */
    public Phone(String serialNumber) 
        this.serialNumber = serialNumber;
    

    public String getSerialNumber() 
        return serialNumber;
    


    public String getName() 
        return name;
    

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

    public Integer getPrice() 
        return price;
    

    public void setPrice(Integer price) 
        this.price = price;
    


    @Override
    public String toString() 
        return "Phone" +
                "serialNumber='" + serialNumber + '\\'' +
                ", name='" + name + '\\'' +
                ", price=" + price +
                '';
    

    /**
     * 重写,
     *      如果希望改变对象的属性,其对象的hash值不会发生变化
     *      则需要以不容易被改变的属性用来计算hashCode值和重写equals方法
     *
     */
    @Override
    public boolean equals(Object o) 
        if (this == o) return true;
        if (!(o instanceof Phone)) return false;
        Phone phone = (Phone) o;
        return getSerialNumber().equals(phone.getSerialNumber()) ;
    

    @Override
    public int hashCode() 
        return Objects.hash(getSerialNumber());
    

将Phone 对象作为key放入map

 public static void main(String[] args) 
        Map map = new HashMap();
        Phone phone = new Phone(UUID.randomUUID().toString());
        phone.setName("华为");
        phone.setPrice(8888);
        map.put(phone, "val");
        System.out.println("第一次获取 :" + map.get(phone));
        phone.setName("小米");
        System.out.println("第二次获取 :" + map.get(phone));
    

运行结果

总结

  1. quals相同hashCode一定相同;
  2. hashCode相同equals不一定相同;
  3. hashCode不相同则对象一定不相同;
  4. hashCode相同则对象不一定相同;
  5. 对象相同,则hashCode一定相同;
  6. 对象不相同,则hashCode不一定相同;
  7. 所有参与计算 hashCode() 返回值的关键属性,都应该用于作为 equals() 比较的标准;
  8. hashCode方法判断的是对象的内存地址,不同对象地址一定不同。也可用==
  9. equals方法判断两个对象的值是否相等。
  10. 当把某个类的对象当成 HashMap的 key,或将这个类的对象放入 HashSet 中保存时,重写hashCode时尽量使用不可变的属性。
  11. 所有参与计算 hashCode() 返回值的关键属性,都应该用于作为 equals() 比较的标准。
  12. 在HashSet添加对象的时候,会先判断hashCode是否一致,如果一致则再调用equals方法,判断内容是否一致。
  13. 如果自定义对象未重写 hashCode方法,则会使用父类Object的hashCode,Object的hashCode每次new一个对象就会有不同的hash值,如果不重写,即使相同的对象也能被添加。
  14. 即使重写了hashCode方法,当存储对象时也会有几率返回相同的hash值,也就是哈希碰撞,此时就需要通过equals方法来判断对象内容是否相同,相同:则不添加;不相同:则添加到相同索引下的链表。

以上是关于SpringBoot 重写hashCode方法和equals方法的主要内容,如果未能解决你的问题,请参考以下文章

为什么要重写equals()方法 和 hashCode()方法

JAVA中重写equals方法为啥要重写hashcode方法说明

为啥要重写toString方法和hashcode方法

java 集合中重写hashCode方法和重写equals方法啥关系?

Java重写hashCode()和equals()方法

hashcode和equals方法