天天看点

向HashSet中添加自定义对象如何区别添加的对象

一、问题来源

当向HashSet中添加String类型的数据时,如果同时添加两个相同的字符串,那么实际上只能添加一个,因为HashSet不允许添加相同的元素.

二、问题分析

问题来了,HashSet是如何区别两个对象(String类型的数据是引用数据类型)的?

浅显的说,String重写了Object类的equals方法和hashCode方法.因此,在比较的时候,HashSet调用了String类中重写的equals方法和hashCode方法.因此会按照String类中定义的判断方式来比较对象是否相同.

判断两个对象是否相等的方式

  1. 首先两个对象的hashCode是否相等,假如相等的话,那么就认为两个对象相等
  2. 假如两个对象的hashCode值不等,那么就通过equals方法来判断两个对象是否相等.默认的equals方法是比较两个对象指向的对象在内存中存储的地址的值是否相等.

现在需求 : 需要利用对象里面的值来判断是否相等

  1. 首先需要重写hashCode方法.(必须重写hashCode方法!)
  2. 其次需要重写equals方法.

假如只重写equals方法会出现什么情况呢?

只重写equals方法,那么重写的equals方法是无效的!

原因是添加对象时首先会调用父类Object类中的hashCode方法(本地方法,无法根据散列码得到对象的内存地址,但实际上,hashcode是根据对象的内存地址经哈希算法得来的).因此两个对象的hashCode的值是不相等的!这样就会认为这是两个不同的元素.

三、代码演示

了解了这些东西,下面开始自己定义一个类,来添加自定义的类

Student类(未重写equals和hashCode方法)

package cn.com.clearlight.setframe.set.bean;

import java.util.Objects;

public class Student implements Comparable<Student>{
    private String name;
    private int age;

    public Student() {
    }

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

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}      

HashSet实体类

package cn.com.clearlight.setframe.set;

import cn.com.clearlight.setframe.set.bean.Student;

import java.util.HashSet;

public class HashSetForEquals {
    public static void main(String[] args) {
        HashSet<Student> hs = new HashSet<>();

        hs.add(new Student("a", 1));
        hs.add(new Student("a", 1));
        hs.add(new Student("ab", 1));

        for (Student s :
                hs) {
            System.out.println(s.getName() + " : " + s.getAge());
        }
    }
}      

输出结果:

ab : 1

a : 1

a : 1

相同元素仍然被添加进去,原因是没有在Student类中自定义自己想要比较的equals方法,因此调用父类Object中的equals方法.比较的是两个对象的引用地址是否相同,new出两个对象,所以地址肯定不相同,因此可以添加两个看上去一样的对象!

重写hashCode方法

public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + age;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }      

重写equals方法

@Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        Student other = (Student) obj;
        if (age != other.age) {
            return false;
        }
        if (name == null) {
            if (other.name != null) {
                return false;
            }
        } else if (!name.equals(other.name)) {
            return false;
        }
        return true;
    }      
首先比较两个对象是否指向同一个内存地址.之后在使用自己自定义方法通过年龄,姓名是否相同来确定两个是否相等.

此时运行实体类结果为:

a : 1

ab : 1

四、总结

  1. ​​为什么重写equals一定要重写hashcode?​​
  2. ​​详解重写equals()方法就必须重写hashCode()方法的原因​​