欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

当集合中的对象要去重时,为什么要重写hashCode和equals方法

程序员文章站 2024-03-25 22:07:16
...

当集合中存放的是对象时,如果要去重则需要重写hashCode和equals方法。

网上这类文章博客有很多,写一下自己的理解。

public class Student{
	private long id;
	private String name;
	private int age;
	private String address;
	
	public Student(){}
	
	

    public Student(long id, String name, int age, String address) {
		super();
		this.id = id;
		this.name = name;
		this.age = age;
		this.address = address;
	}

    

	@Override
	public String toString() {
		return "Student [id=" + id + ", name=" + name + ", age=" + age + ", address=" + address + "]";
	}



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

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

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    
    public static void main(String[] args){
    	Student s1 = new Student(1L,"肖战",15,"浙江");
    	Student s2 = new Student(2L,"王一博",15,"湖北");
    	Student s3 = new Student(3L,"杨紫",17,"北京");
    	Student s4 = new Student(4L,"李现",17,"浙江");
    	Student s5 = new Student(1L,"肖战",15,"浙江");
    	List<Student> students = new ArrayList<>(4);
    	students.add(s1);
    	students.add(s2);	
    	students.add(s3);
    	students.add(s4);
    	students.add(s5);
    	List<Student> streamStudents = testDistinct1(students);
    	streamStudents.forEach(System.out::println);
    }
    
    /**
     * 集合去重(对象的去重需要重写hashCode和equals方法)
     * @param tudents
     * @return
     */
    private static List<Student> testDistinct1(List<Student> students){
    	return students.stream().distinct().collect(Collectors.toList());
    }
}

如上:创建了一个Student类并重写了他的equals和hashcode方法,执行下main方法,执行结果:

Student [id=1, name=肖战, age=15, address=浙江]
Student [id=2, name=王一博, age=15, address=湖北]
Student [id=3, name=杨紫, age=17, address=北京]
Student [id=4, name=李现, age=17, address=浙江]

结果集被成功去重了。

这时我们将重写的hashCode和equals方法去掉或者只去掉其中一个会发现都不会成功去重

原因是:

我们的对象默认继承的是Object类,原始的equals方法比对的是值,也就是堆内存值的地址,当我们创建student s5的时候,看上去是与student s1是一模一样的,但他们的引用地址不一样,所以存在堆内存地址中是两个值。看一下equals的源码

public static boolean equals(Object a, Object b) {
        return (a == b) || (a != null && a.equals(b));
    }

可以看到他既做了a==b的判断也做了equals的判断,既判断他两个对象是否相同也判断他两个值引用地址是否相同,那我们重写的逻辑就是判断对象里的值是否完全相同,一般用工具自带的生成equals方法就能实现-。- 还真的是很方便呢。

那为什么重写了equals方法还要重写hashCode方法才能使集合去重成功呢?

先看一下hashCode的源码

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;
    }

可以看到传参是一个对象数组,返回值是一个int,也就是我们说的hash值,每个值引用的hash值是不一样的,hash值不一样也不能判定s1和s5是一模一样的。

引申出的一个问题:hashcode里的31是什么,这个乘积的作用是什么,为什么是31而不是30或是29或其他的数

参考文章:https://www.cnblogs.com/0813lichenyu/p/8367103.html

看来选择31作为乘积还是因为性能原因

再回到我们的问题上,已经知道不重写hashcode方法会使我们的的去重不生效,那应该如何重写呢,其实跟equals一样,只要对对象中每个值的hash值做判断就可以了,如果全都一样则对象的hash值也是一样的。也可以通过工具自动生成...

好了 这下我们已经知道怎么重写hashcode和equals方法了,回过头我们再去看一下官方jdk api上对hashcode和equals的描述

当集合中的对象要去重时,为什么要重写hashCode和equals方法

当集合中的对象要去重时,为什么要重写hashCode和equals方法

 

其实官方文档已经说得很清楚了,重写equals方法时最好同时重写hashcode方法QwQ。

好了,这就是为什么当集合中的对象要去重时,要重写hashCode和equals方法的原因