hashCode(),equals() 示例讲解
两个对象的 hashCode()相同,equals()也为 true,两个对象一定相同吗?
我们写一个类来了解一下hashCode() 和 equals()
public class Person {
private String name;
private Person(String name) {
this.name = name;
}
@Override
public int hashCode() {
return 1;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Person) {
Person p = (Person) obj;
return this.name.equals(p.name);
}
return false;
}
这里自定义了一个Person类并且重写了hashCode()和equals()方法。其中hashCode()方法始终返回 1(只是测试使用,实际不应该如此设计),而equals()方法则通过比较两个Person对象的属性 name(String类型)的值 返回布尔值。
接下来 我们来写一段测试代码。
public static void main(String[] args) {
String str = "a";
Person a = new Person(str);
Person b = new Person(str);
System.out.println(a);
System.out.println(b);
System.out.println("a.hashCode: "+a.hashCode()+" b.hashCode: "+b.hashCode());
System.out.println("equals: " +a.equals(b));
System.out.println(a==b);
}
运行结果:

这里我们看出即使重写了hashCode()和equals()方法,使得对象a和b的hash值相同,equals方法也为true。a==b 依然为false。由此可见"==“运算符实际是比较的是地址引用, 引用对象指向的地址如果相同,则返回true 。并不依赖我们重写了的hashCode()和equals()方法。所以简单的考虑,两个new出来的对象,一定是存在不同的内存地址中。所以判断a==b一定是false。那么也就是说 hash值相同,equals比较也为true的两个对象不一定就是相等的,这里的相等指的是”=="运算符,而不是equals()方法。
那么 重写hashcode() 和equals()有何作用呢;这里我们引用一个容器 HashSet。Set容器要求存入Set的每个元素都必须是唯一的,Set不保存重复元素。而存入HashSet的元素必须定义hashCode()方法 (出自thinking in java)
我们添加几行代码
public static void main(String[] args) {
String str = "a";
Person a = new Person(str);
Person b = new Person(str);
HashSet<Person> set = new HashSet<>();
set.add(a);
set.add(b);
System.out.println(a);
System.out.println(b);
System.out.println("a.hashCode: "+a.hashCode()+" b.hashCode: "+b.hashCode());
System.out.println("equals: " +a.equals(b));
System.out.println(a==b);
System.out.println(set+" set.size= "+set.size());
}
运行结果:

运行结果显示 HashSet里只存放了一个对象,即Set容器认为Person a 和Person b为同一个对象,那么它是怎么判断的呢?
我们再次修改代码,这次注释掉重写的hashCode()方法
public class Person {
private String name;
public Person(String name) {
this.name = name;
}
// @Override
// public int hashCode() {
//
// return 1;
// }
@Override
public boolean equals(Object obj) {
if (obj instanceof Person) {
Person p = (Person) obj;
return this.name.equals(p.name);
}
return false;
}
public static void main(String[] args) {
String str = "a";
Person a = new Person(str);
Person b = new Person(str);
HashSet<Person> set = new HashSet<>();
set.add(a);
set.add(b);
System.out.println(a);
System.out.println(b);
System.out.println("a.hashCode: "+a.hashCode()+" b.hashCode: "+b.hashCode());
System.out.println("equals: " +a.equals(b));
System.out.println(a==b);
System.out.println(set+" set.size= "+set.size());
}
}
运行结果:

现在Set中有两个Person对象,也就是说Set认为Person a 和 Person b是两个不同的对象了。因为a和b的HashCode值不同。可是我们已经重写了equals()方法,我们希望通过属性name来确定对象是否相同,即name相同的对象就认为是同一个对象。这显然是不符合我们的预期的,这个问题我们稍后会继续讨论。接下来考虑这种情况:如果equals()方法返回false,但hashcode相同的对象,HashSet会如何判断呢?
我们接着修改代码,这次给a和b的name属性赋不同的值
public main(String[] args) {
Person a = new Person("a");
Person b = new Person("b");
HashSet<Person> set = new HashSet<>();
set.add(a);
set.add(b);
System.out.println(a);
System.out.println(b);
System.out.println("a.hashCode: "+a.hashCode()+" b.hashCode: "+b.hashCode());
System.out.println("equals: " +a.equals(b));
System.out.println(a==b);
System.out.println(set+" set.size= "+set.size());
}
运行结果:

现在我们看到Set中存了两个Person对象,它们的HashCode相同,但是name值不同。HashSet认为是不同的两个对象。那么也就是说HashSet在判断两个对象是否相同时,首先会判断它们的hashCode是否相同。hashCode不同则为不同的对象。如果hashCode相同则接着调用equals()方法,equals方法为true则为同一个对象,false则为不同的对象。
回到刚才的的问题,如果我们需要通过equals()方法来判断对象是否相等,则应该设计合适的hashCode()方法,即遵循equals()为true的两个对象返回相同的hashCode。为保持这个特性。我们需要简单修改一下Person类的hashCode()方法,返回属性name的hashCode即可。
@Override
public int hashCode() {
return name.hashCode();//返回属性name的hashCode
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Person) {
Person p = (Person) obj;
return this.name.equals(p.name);
}
return false;
}
public static void main(String[] args) {
Person a = new Person("a");
Person b = new Person("a");
HashSet<Person> set = new HashSet<>();
set.add(a);
set.add(b);
System.out.println(a);
System.out.println(b);
System.out.println("a.hashCode: "+a.hashCode()+" b.hashCode: "+b.hashCode());
System.out.println("equals: " +a.equals(b));
System.out.println(a==b);
System.out.println(set+" set.size= "+set.size());
}
运行结果:

(61为97的16进制表示,toString()方法未重写,"@"后打印hashCode的16进制值)
Set中仅存在一个对象,这样设计hashCode()方法,就遵循上述原则。即equals()为true的两个对象hashCode一定相同。
最后贴一下Java API对于HashCode()方法的解释
HashCode()方法:返回对象的哈希码值。 这个方法是对于支持哈希表有益的,
*例如由提供的哈希表
* {@link java.util.HashMap}。每当在同一个对象上多次调用它时
*执行Java应用程序,{@code hashCode}方法
*必须始终返回相同的整数,前提是没有信息
*用于{@code equals}对象的比较被修改。
*这个整数不需要一次执行就保持一致
*应用程序到同一应用程序的另一个执行。
如果两个对象根据{@code equals(Object)}相等
*方法,然后在每个上调用{@code hashCode}方法
*这两个对象必须产生相同的整数结果。
如果两个对象不相等则不需要
*根据{@link java.lang.Object#equals(java.lang.Object)}
*方法,然后在每个上调用{@code hashCode}方法
*两个对象必须产生不同的整数结果。然而
*程序员应该意识到产生不同的整数结果
*对于不等对象可能会提高哈希表的性能。
本文深入探讨了hashCode()和equals()方法在Java中的实现与应用,通过实例解析了这两个方法的作用及如何影响HashSet容器中对象的唯一性判断。
4511

被折叠的 条评论
为什么被折叠?



