简介
默认情况下,对象的equal()和hashcode()方法是调用Object类的equals方法和hashcode方法
public booleanequals(Object obj) {
return (this== obj);
}
public native int hashCode();
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
从上面代码可以看出Object类的equals()和hashCode()方法效果一样,都是根据对象的hash值来判断两个对象是否相等。hash值如何生成的可以看这里:点击
例子
@Test
public void testEqualsAndHashCode() {
User user1 = new User();
User user2 = new User();
System.out.println(user1.hashCode());
System.out.println(user2.hashCode());
if(user1.equals(user2)) {
System.out.println("相等");
}else {
System.out.println("不相等");
}
}
输出:
1144748369
340870931
不相等
因为每个对象生成的哈希值不一样,所以equals方法返回false,
如何实现相等呢?通过重写equals
重写equals
定义一个User对象有多个属性值姓名、年龄、身份证;我们需要两个对象的各项属性值一样的就认为这两个对象是相等的;那么此时我们就需要重写equals方法了;
代码如下:
class User {
private String name;// 姓名
private String IdCard;// 身份证
private int age;// 年龄
@Override
public boolean equals(Object obj) {
if (obj instanceof User) {
User user = (User) obj;
if (user.getIdCard().equals(this.IdCard) && user.getName().equals(this.name) && user.getAge() == this.age) {
return true;
} else {
return false;
}
} else {
return false;
}
}
//省略get和set方法
}
(1)测试equals()
@Test
public void testEqualsAndHashCode() {
User user1 = new User();
user1.setName("dd");
user1.setAge(18);
user1.setIdCard("d30");
User user2 = new User();
user2.setName("dd");
user2.setAge(18);
user2.setIdCard("d30");
System.out.println("user1.equals(user2)=" + user1.equals(user2));
}
输出:
user1.equals(user2)=true
因为user1和user2的name和idCard和age都一样,自然equals返回true
(2)把他们存储到set中
Set set =new HashSet();
set.add(user1);
set.add(user2);
System.out.println(set.size());
输出:
2
问题来了明明user1和user2两个对象是equals的那么为什么把他们放到set中会有两个对象(Set特性是不允许重复数据的)?
原因是user1和user2的hashcode 不一样导致的;
重写hashcode()
上面例子可以知道因为我们没有重写父类(Object)的hashcode方法,Object的hashcode方法会通过使用线程局部状态来实现Marsaglia's xor-shift随机数生成(详情看这点击)相应的hashcode;所以每个对象的hash值都不一样。
下面通过重写hashcode()方法,根据user对象内容生成hash值
@Override
public int hashCode() {
int result = name.hashCode();
result = 31 * result + IdCard.hashCode();
result = 31 * result + age;
return result;
}
再测试,结果如下:
user1.equals(user2)=true
1
因为user1和user2的name和idCard和age都一样,自然hashcode也一样
equals 方法的坑
Object 的 equals 方法容易抛空指针异常,应使用常量或确定有值的对象来调用equals。
比如: "test".equals(object);
这里object可能为null
总结
上面例子说明了重写equals方法后必须重写hashCode方法
扩展:String的hashCode()计算公式为:s[0]31^(n-1) + s[1]31^(n-2) + … + s[n-1]
关于hashCode()计算过程中,为什么使用了数字31,主要有以下原因(引用这里的结论点击):
1、使用质数计算哈希码,由于质数的特性,它与其他数字相乘之后,计算结果唯一的概率更大,哈希冲突的概率更小。
2、使用的质数越大,哈希冲突的概率越小,但是计算的速度也越慢;31是哈希冲突和性能的折中,实际上是实验观测的结果。
3、JVM会自动对31进行优化:31 * i == (i << 5) – i
具体原因我也不太懂