重写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.

  1. equals相同hashCode一定相同;
  2. hashCode相同equals不一定相同;
  3. hashCode不相同则对象一定不相同;
  4. hashCode相同则对象不一定相同;
  5. 对象相同,则hashCode一定相同;
  6. 对象不相同,则hashCode不一定相同;
  7. 所有参与计算 hashCode() 返回值的关键属性,都应该用于作为 equals() 比较的标准;

2.

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

猜你喜欢

转载自blog.csdn.net/packge/article/details/126828302