我们再用JAVA创建自己的类的时候,一种比较常见的覆盖就是覆盖Object中的equals()方法和hashCode()方法。如果不这样做的话,就很可能违反Object.hashCode()的通用约定,从而在利用自己建的类构建需要Hash化的集合的正常工作。其中有一条约定很重要:
如果两个对象利用equals方法比较是相等的,那么这两个对象必须能返回同样的hashCode。
这一点很好理解,就比如拿Set来说,Set的特点就是元素是无须的且不可重复。那么这里面所谓的重复的定义,就是需要程序员通过equals去定义的,既然你覆盖了equals()方法,那么也就是你已经定义了重复的概念。那么如果equals()返回了True就意味着hashCode()必须返回一样的散列码。
举个栗子,我这里新建了三个类:
1.User:没有覆盖equals也没有覆盖hashCode
package com.blog.hash;2.EqualUser:只覆盖equals方法
public class User {
private int id;
private String name;
public User(int id, String name) {
// TODO Auto-generated constructor stub
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package com.blog.hash;3.HashUser:覆盖了equals和hashCode方法
public class EqualUser {
private int id;
private String name;
public EqualUser(int id, String name) {
// TODO Auto-generated constructor stub
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
EqualUser other = (EqualUser) obj;
if (id != other.id)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
package com.blog.hash;除此之外还写了一个测试类MyTest
public class HashUser {
private int id;
private String name;
public HashUser(int id, String name) {
// TODO Auto-generated constructor stub
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + id;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
HashUser other = (HashUser) obj;
if (id != other.id)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
package com.blog.hash;在方法withoutOveride中,新建了两个User对象,因为User没有覆盖equals也没有覆盖hashCode,所以调用的是Object的方法。因此equals比较的是引用,所以equals()返回false,打印结果为:
import java.util.HashSet;
import java.util.Set;
import org.junit.Test;
public class MyTest {
@Test
public void withoutOveride() {
User u1 = new User(1, "mike");
User u2 = new User(1, "mike");
Set<User> set = new HashSet();
set.add(u1);
set.add(u2);
System.out.println(u1.equals(u2));
for (User u : set) {
System.out.println(u.getId() + u.getName());
}
}
@Test
public void withAllOveride() {
HashUser u1 = new HashUser(1, "mike");
HashUser u2 = new HashUser(1, "mike");
Set<HashUser> set = new HashSet();
set.add(u1);
set.add(u2);
System.out.println(u1.equals(u2));
for (HashUser u : set) {
System.out.println(u.getId() + u.getName());
}
}
@Test
public void withEqualOveride() {
EqualUser u1 = new EqualUser(1, "mike");
EqualUser u2 = new EqualUser(1, "mike");
Set<EqualUser> set = new HashSet();
set.add(u1);
set.add(u2);
System.out.println(u1.equals(u2));
for (EqualUser u : set) {
System.out.println(u.getId() + u.getName());
}
}
}
false
1mike
1mike
这里u1和u2明显是同一个值,只是引用不一样,所以在实际使用的时候会覆盖equals方法。
在方法withEqualOveride中,建立两个EqualUser对象,由于覆盖了equals方法而没有覆盖hashCode,所以调用equals时返回true,而hashCode还是Object的方法,打印结果为:
true
1mike
1mike
这样在set中就存在了重复的值,所以会导致在使用hash话的集合的时候出现问题。
最后,在方法withAllOveride中,由于覆盖了equals和hashCode方法,因此调用equals方法的时候返回的是true,同时set中没有重复的值,打印结果为:
true
1mike