引言
java基础系列之前已经剖析过好几个java里面比较关键的关键字了,今天我们来讲讲容易被人忽视的equals方法和hashcode方法。为什么说容易被人忽视,因为这两货就算你理他,也不会出什么问题,java的上帝类Object自带equals方法和hashcode方法,所以不用你操心,然鹅只要你根据业务需求来重写equals方法,就很有可能出错,今天我们就来看看这两个方法。
equals方法
每个对象默认都是用上帝类Object的equals方法:
public boolean equals(Object obj) {
return (this == obj);
}
非常简单粗暴的直接判断是否为当前对象,如果不是直接返回false。如果你自定义的对象要判断两个对象是否相等直接用父类的equals方法是不行的,做个试验:
public class TestEquals {
private String testName;
private Integer age;
//省略 getter setter方法
}
TestEquals test1 = new TestEquals();
test1.setTestName("test");
test1.setAge(10);
TestEquals test2 = new TestEquals();
test2.setTestName("test");
test1.setAge(10);
System.out.println(test1.equals(test2));
上面100%返回false,因为是两个对象,但是在业务上很可能是需要返回true的,因为数据都一样需要它是equal的。所以在实际开发场景下重写equals方法的概率还是很大的。
在重写equals方法时我们需要注意遵守以下条件:
- 自反性:x.equals(x)必须返回true。
- 对称性:x.equals(y)与y.equals(x)的返回值必须相等。
- 传递性:x.equals(y)为true,y.equals(z)也为true,那么x.equals(z)必须为true。
- 一致性:如果对象x和y在equals()中使用的信息都没有改变,那么x.equals(y)值始终不变。
- 对于任何非空引用值x,x.equal(null)应返回false。
重写equals方法最佳实践可以按照以下步骤来做:
- 使用
==
操作符判断是否当前对象(判断参数是否为该对象的引用),如果是直接返回true。 - 使用
instanceof
判断是否为目标类的实例如果不是直接返回false。 - 根据你的业务逻辑使用
Objects.equals
方法判断各个字段是否相同。 - 重写equals方法后必须重写hashcode方法。
hashcode方法
hashcode方法很多初学者一开始都很迷惑这个方法有什么用,当你看了java的集合之后应该会找到答案。对hashcode在集合类里面是有作用的特别是底层数据结构是hash表的集合类如HashMap、HashSet等。
以HashMap为例,在往HashMap中存Element时,首先会先调用key的hashcode方法,并且进一步根据HashMap的hash方法进行再哈希,进而找到需要存放的地址。并且HashMap的元素是不重复的,如果重复插入相同的对象是value是会被覆盖的。HashMap的get方法也是一样先要通过hash方法找到Element在哈希表上位置。上面说到重写了equals方法必须重写hashcode方法是因为如果只重写equals方法会打破元素不重复的规则,重复插入相同的key并不会在哈希表上找到同一个位置,原因是hashcode不一致,最后将会导致相同的几个元素在哈希表上的多个位置上出现。如果你equals方法都不重写估计你put进HashMap的元素再get出来很可能返回NULL。有兴趣的同学可以自己去试验下。多说一句,有的人说为啥我如果key是String类型就没问题,那是因为String的equals方法和hashcode方法已经被重写过了。
hashcode方法重写的最佳实践
- 定义一个种子数。
- 用该种子数*31+你的字段的hashcode赋值
例如:
public int hashcode() {
int hash = 17;
hash = hash*31+field1.hashcode();
hash = hash*31+filed2.hashcode();
........
}
或者你也可以使用Objects.hashcode
方法,把你要比较的fields当做参数传进去就可以了。