- 前言
- 示例代码结构
- 情景演示
- 情景一:不重写equals与hashCode
- 普通情况下使用
- 在哈希存储结构下使用
- 情景二:仅重写equals
- 普通情况下使用
- 在哈希存储结构下使用
- 情景三:仅重写hashCode
- 普通情况下使用
- 在哈希存储结构下使用
- 情景四:同时重写hashCode与equals
- 普通情况下使用
- 在哈希存储结构下使用
- 总结
- 补充:String
在使用集合存储非Java基本类型与String的对象涉及到排序或者其他操作时,我们总是会同时重写hashCode与equals方法,但是我一直不明白其中的具体原因,今天扒一下。
示例代码结构注: String类型虽然也为引用类型,但是Java中已经帮我们重写完毕hashCode与equals方法。
结构
Student类
注:为了便于查看控制台信息,所以先重写了toString方法打印字段信息。
package bean;
public class Student {
private String stuNumber;
private String name;
public Student(){
}
public Student(String stuNumber, String name){
this.stuNumber=stuNumber;
this.name=name;
}
public String getStuNumber() {
return stuNumber;
}
public void setStuNumber(String stuNumber) {
this.stuNumber = stuNumber;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "stuNumber:"+stuNumber+" name:"+name+"n";
}
}
main方法调用类
public class Main {
public static void main(String[] args) {
//...
}
}
情景演示
情景一:不重写equals与hashCode
普通情况下使用
测试
public static void main(String[] args) {
Student obj_1 = new Student("2021", "小黑");
Student obj_2 = new Student("2021", "小黑");
//哈希值
System.out.println("---------------------");
System.out.println("obj_1 hashCode:"+obj_1.hashCode());
System.out.println("obj_2 hashCode:"+obj_2.hashCode());
System.out.println("---------------------");
//运算符'==' 仅仅是比较了两个变量对象的地址(java运行时数据区的堆地址)
System.out.println(obj_1 == obj_2);
System.out.println(obj_1.equals(obj_2));
}
输出
--------------------- obj_1 hashCode:460141958 obj_2 hashCode:1163157884 --------------------- false false Process finished with exit code 0在哈希存储结构下使用
测试
public static void main(String[] args) {
Student obj_1 = new Student("2021", "小黑");
Student obj_2 = new Student("2021", "小黑");
//哈希值
System.out.println("---------------------");
System.out.println("obj_1 hashCode:"+obj_1.hashCode());
System.out.println("obj_2 hashCode:"+obj_2.hashCode());
System.out.println("---------------------");
//不可以重复存在的HashSet集合
HashSet students = new HashSet<>();
//这里因为没有重写hashCode与equals,所以无法判断是否重复
students.add(obj_1);
students.add(obj_2);
//遍历
for (Student temp:students) {
System.out.printf(temp.toString());
}
}
输出
--------------------- obj_1 hashCode:460141958 obj_2 hashCode:1163157884 --------------------- stuNumber:2021 name:小黑 stuNumber:2021 name:小黑 Process finished with exit code 0情景二:仅重写equals
重写代码
@Override
public boolean equals(Object o) {
//如果 java运行时数据区的堆地址相等 则表示同一对象 肯定相等
if (this == o) return true;
//如果为空 或者 不是一个类型 则肯定不相等
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
//判断属性值是否相等,这里采用了工具类Objects的方法判断
return Objects.equals(stuNumber, student.stuNumber) && Objects.equals(name, student.name);
}
普通情况下使用
测试
public static void main(String[] args) {
Student obj_1 = new Student("2021", "小黑");
Student obj_2 = new Student("2021", "小黑");
//哈希值
System.out.println("---------------------");
System.out.println("obj_1 hashCode:"+obj_1.hashCode());
System.out.println("obj_2 hashCode:"+obj_2.hashCode());
System.out.println("---------------------");
//运算符'==' 仅仅是比较了两个变量对象的地址(java运行时数据区的堆地址)
//由于这是两个实例对象,在堆上开辟了两个不同的空间,所以地址一定不同
System.out.println(obj_1 == obj_2);
//这里便可以判断值了
System.out.println(obj_1.equals(obj_2));
}
输出
--------------------- obj_1 hashCode:460141958 obj_2 hashCode:1163157884 --------------------- false true Process finished with exit code 0在哈希存储结构下使用
测试
public static void main(String[] args) {
Student obj_1 = new Student("2021", "小黑");
Student obj_2 = new Student("2021", "小黑");
//哈希值
System.out.println("---------------------");
System.out.println("obj_1 hashCode:"+obj_1.hashCode());
System.out.println("obj_2 hashCode:"+obj_2.hashCode());
System.out.println("---------------------");
//不可以重复存在的HashSet集合
HashSet students = new HashSet<>();
//这里虽然重写了equals方法,但是由于采用哈希算法实现的集合首先会判断哈希值重复
//由于没有重写hashCode方法,导致这两个对象即便属性值都相等的情况下,哈希值也是不一样的
//所以这里会直接放入,不再调用equals判断
students.add(obj_1);
students.add(obj_2);
//遍历
for (Student temp:students) {
System.out.printf(temp.toString());
}
}
输出
--------------------- obj_1 hashCode:460141958 obj_2 hashCode:1163157884 --------------------- stuNumber:2021 name:小黑 stuNumber:2021 name:小黑 Process finished with exit code 0情景三:仅重写hashCode
重写代码
@Override
public int hashCode() {
//使用工具类Objects的hash方法来将每个字段的哈希值按照算法组合返回 本实例的最终哈希值
return Objects.hash(stuNumber, name);
}
普通情况下使用
测试
public static void main(String[] args) {
Student obj_1 = new Student("2021", "小黑");
Student obj_2 = new Student("2021", "小黑");
//哈希值
System.out.println("---------------------");
System.out.println("obj_1 hashCode:"+obj_1.hashCode());
System.out.println("obj_2 hashCode:"+obj_2.hashCode());
System.out.println("---------------------");
//运算符'==' 仅仅是比较了两个变量对象的地址(java运行时数据区的堆地址)
System.out.println(obj_1 == obj_2);
System.out.println(obj_1.equals(obj_2));
}
输出
--------------------- obj_1 hashCode:48427782 obj_2 hashCode:48427782 --------------------- false false Process finished with exit code 0在哈希存储结构下使用
测试
public static void main(String[] args) {
Student obj_1 = new Student("2021", "小黑");
Student obj_2 = new Student("2021", "小黑");
//哈希值
System.out.println("---------------------");
System.out.println("obj_1 hashCode:"+obj_1.hashCode());
System.out.println("obj_2 hashCode:"+obj_2.hashCode());
System.out.println("---------------------");
//不可以重复存在的HashSet集合
HashSet students = new HashSet<>();
//这里由于重写了hashCode,两个属性相同的对象的哈希值根据判断是相同的(因为每个对象的哈希值是其属性字段的哈希值按照一定的算法组合而成)
//所以在执行完哈希值重复性校验后,得到哈希值相同的结果,开始调用equals方法具体判断,然而我们没有重写equals方法,导致默认判断堆地址,所以依旧放入对象
students.add(obj_1);
students.add(obj_2);
//遍历
for (Student temp:students) {
System.out.printf(temp.toString());
}
}
输出
--------------------- obj_1 hashCode:48427782 obj_2 hashCode:48427782 --------------------- stuNumber:2021 name:小黑 stuNumber:2021 name:小黑 Process finished with exit code 0情景四:同时重写hashCode与equals
重写代码
@Override
public boolean equals(Object o) {
//如果 java运行时数据区的堆地址相等 则表示同一对象 肯定相等
if (this == o) return true;
//如果为空 或者 不是一个类型 则肯定不相等
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
//判断属性值是否相等,这里采用了工具类Objects的方法判断
return Objects.equals(stuNumber, student.stuNumber) && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
//使用工具类Objects的hash方法来将每个字段的哈希值按照算法组合返回 本实例的最终哈希值
return Objects.hash(stuNumber, name);
}
普通情况下使用
测试
public static void main(String[] args) {
Student obj_1 = new Student("2021", "小黑");
Student obj_2 = new Student("2021", "小黑");
//哈希值
System.out.println("---------------------");
System.out.println("obj_1 hashCode:" + obj_1.hashCode());
System.out.println("obj_2 hashCode:" + obj_2.hashCode());
System.out.println("---------------------");
//运算符'==' 仅仅是比较了两个变量对象的地址(java运行时数据区的堆地址)
System.out.println(obj_1 == obj_2);
System.out.println(obj_1.equals(obj_2));
}
输出
--------------------- obj_1 hashCode:48427782 obj_2 hashCode:48427782 --------------------- false true Process finished with exit code 0在哈希存储结构下使用
测试
public static void main(String[] args) {
Student obj_1 = new Student("2021", "小黑");
Student obj_2 = new Student("2021", "小黑");
//哈希值
System.out.println("---------------------");
System.out.println("obj_1 hashCode:" + obj_1.hashCode());
System.out.println("obj_2 hashCode:" + obj_2.hashCode());
System.out.println("---------------------");
//不可以重复存在的HashSet集合
HashSet students = new HashSet<>();
students.add(obj_1);
students.add(obj_2);
//遍历
for (Student temp:students) {
System.out.printf(temp.toString());
}
}
输出
--------------------- obj_1 hashCode:48427782 obj_2 hashCode:48427782 --------------------- stuNumber:2021 name:小黑 Process finished with exit code 0总结
- hashCode主要用于提升查询效率,来确定在散列结构中对象的存储地址;
采取重写hashCode方法,先进行hashCode比较,如果不同,那么就没必要在进行equals的比较了,这样就大大减少了equals比较的次数,这对比需要比较的数量很大的效率提高是很明显的,一个很好的例子就是在集合中的使用。 - 重写equals()必须重写hashCode(),二者参与计算的自身属性字段应该相同;
- 哈希类型的存储结构,添加元素重复性校验的标准就是先取hashCode值,如果不相等直接放入,不必再判断equals(),如果哈希值相等再判断equals();
测试
public static void main(String[] args) {
String s_1 = new String("你好");
String s_2 = new String("你好");
String s_3 = "你好";
String s_4 = "你好";
//哈希值
System.out.println("---------------------");
System.out.println("s_1 hashCode:" + s_1.hashCode());
System.out.println("s_2 hashCode:" + s_2.hashCode());
System.out.println("s_3 hashCode:" + s_3.hashCode());
System.out.println("s_4 hashCode:" + s_4.hashCode());
System.out.println("---------------------");
System.out.println(s_1.equals(s_2));
System.out.println(s_1.equals(s_3));
//不可以重复存在的HashSet集合
HashSet s = new HashSet<>();
s.add(s_1);
s.add(s_2);
s.add(s_3);
s.add(s_4);
//遍历
for (String a:s) {
System.out.printf(a);
}
}
输出
--------------------- s_1 hashCode:652829 s_2 hashCode:652829 s_3 hashCode:652829 s_4 hashCode:652829 --------------------- true true 你好 Process finished with exit code 0
参考阅读:
为什么重写equals方法,还必须要重写hashcode方法



