向HashSet中添加自定义对象如何区别添加的对象
原创
©著作权归作者所有:来自51CTO博客作者ClearlightY的原创作品,请联系作者获取转载授权,否则将追究法律责任
一、问题来源
当向HashSet中添加String类型的数据时,如果同时添加两个相同的字符串,那么实际上只能添加一个,因为HashSet不允许添加相同的元素.
二、问题分析
问题来了,HashSet是如何区别两个对象(String类型的数据是引用数据类型)的?
浅显的说,String重写了Object类的equals方法和hashCode方法.因此,在比较的时候,HashSet调用了String类中重写的equals方法和hashCode方法.因此会按照String类中定义的判断方式来比较对象是否相同.
判断两个对象是否相等的方式
- 首先两个对象的hashCode是否相等,假如相等的话,那么就认为两个对象相等
- 假如两个对象的hashCode值不等,那么就通过equals方法来判断两个对象是否相等.默认的equals方法是比较两个对象指向的对象在内存中存储的地址的值是否相等.
现在需求 : 需要利用对象里面的值来判断是否相等
- 首先需要重写hashCode方法.(必须重写hashCode方法!)
- 其次需要重写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方法! 重写之后,我们就可以根据我们的需求来添加元素.
[参考文章]
- 为什么重写equals一定要重写hashcode?
- 详解重写equals()方法就必须重写hashCode()方法的原因