一、问题来源

当向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


四、总结

通过向HashSet中添加自定义对象的实例,我们知道必须要在自定义的类中重写hashCode和equals方法! 重写之后,我们就可以根据我们的需求来添加元素.


[参考文章]

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