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

当集合中存放的是对象时,如果要去重则需要重写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的描述

 

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

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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值