1.JDK规定,equals相等的两个对象hashCode也必须相等,这两个方法都是从Object上面继承而来的,通过观察JDK源码可以发现Object的equals方法是对2个对象的地址(逻辑地址,也就是在JVM中映射一个物理地址)比较,而hashCode又是一个本地方法,其实hashCode就是内存中的一个地址,换句话说,2个相等的对象(地址相等)那么他们的hashCode也肯定是相等的,反过来hashCode不相等,equals也一定不相等,但是hashCode相等equals却不一定相等,因为在生成hashCode的时候可能有冲突,还有一种情况就是JDK的String、Integer等等这些类都是重写了equals和hashCode方法的,不同的重写生成的hashCode有可能重复。
但是他们重写的结果依然是:equals相等,hashCode就相等(看源码得出的结论)。一般来讲重写equals都需要重写一下hashCode。
2.举例子:
有了上面的结论,那么举个简单的例子,Java的集合框架中的Set<E>是无序不能重复的,那么如何判断Set中的元素重复性就是equals的问题。
假设我们有一个User类:
public class User {
private String name;
private String password;
//get.set方法
}
现在把User的两个对象添加进一个Set<User>集合:
Set<User> sets = new HashSet<User>();
User u1 = new User("aaa", "aaa");
User u2 = new User("bbb", "bbb");
sets.add(u1);
sets.add(u2);
现在需要再向sets里面添加一个和u1一样的元素,User u3 = new User("aaa", "aaa");你会发现可以添加进去,这是怎么回事呢,u1和u3不是一样吗,其实不是这样的,u1和u3在内存中的地址不一样(分别new出来的两个对象地址不同),那么我们重写User的equals方法,如下:(我们只判断用户名,只要name相等就相等,忽视password)
@Override
public boolean equals(Object obj) {
if(obj == null) {
return false;
}
if(!(obj instanceof User)) {
return false;
}
if(this == obj) {
return true;
}
User u = (User)obj;
if(!(this.name == null ? u.getName() == null : this.name.equals(u.getName()))) {
return false;
}
return true;
}
再测试,发现u3还是能添加斤sets,这又是什么情况,不是说equals相等就是相同的元素,就不能添加进去吗,其实还有hashCode的原因。试想一下我现在sets里面只有u1和u2两个元素,当添加u3的时候,用u3跟u1和u2做equals比较,如果返回值为false就添加进去,如果为有一个为true就表示重复了,这样子是可行的,也是安全的,但是问题就在于如果我的sets里面已经有1w个User了呢,岂不是每一次添加新的User的时候都要比较1~10000次么,对于高并发的系统,这个是不允许的,性能太差。所以就有了hashCode,刚刚说道当sets里面存在u1和u2,当添加u3的时候,他要做遍历equals比较,其实Java不是这样做的,当添加u3的时候,JVM会先调用u3的hashCode方法,返回一个int数字,这个int数字上面说了,是映射一个内存地址,当sets在add u3的时候,他发现这块地址上面没有东西,那就直接添加进sets,不用比较,因为连内存中都不存在u3,那sets里面就肯定不存在了,根本不需要比较,但是,还有一种情况,就是当add u3的时候发现这块地址上面已经存在了一个u3,那是不是就意味着sets里面有u3了呢,答案是否定的,因为根据上面的规定,hashCode相等但是equals不一定相等,那么这时候就需要对sets里面的元素进行比较,如果返回值全都是false,就说明sets里面没有u3,那就添加进去,如果有一个返回值为true就说明已经存在了,就不添加进去,这样子性能就好很多。所以说就需要重写equals的同同时重写hashCode,并且保持一致性,前者相等,后者就必须相等,这是JDK规定,不然二者就没有什么意义了。于是重写hashCode,如下:
@Override
public int hashCode() {
return this.name.hashCode();
}
这样子就表示一个根据name重写,相同的name对应的hashCode一定是相同的。当比较到hashCode相同的时候直进行equals比较,而不同的时候直接添加。
这样一来,再添加name相同的两个User就一定添加不进去了。hashCode就是这样的原理。