1、重写equals方法的要求:
(1)、自反性:对于任何非空引用x,x.equals(x)应该返回true。
(2)、对称性:对于任何引用x和y,如果x.equals(y)返回true,那么y.equals(x)也应该返回true。
(3)、传递性:对于任何引用x、y和z,如果x.equals(y)返回true,y.equals(z)返回true,那么x.equals(z)也应该返回true。
(4)、一致性:如果x和y引用的对象没有发生变化,那么反复调用x.equals(y)应该返回同样的结果。
(5)、非空性:对于任意非空引用x,x.equals(null)应该返回false。
2、instanceof和getClass()的选择
instanceof进行类型检查规则是:你属于该类吗?或者你属于该类的派生类吗?而通过getClass获得类型信息采用==来进行检查是否相等的操作是严格的判断。不会存在继承方面的考虑。
所以重写equals方法时选用getClass()来进行判断对象是否属于类,而不用instanceof,更合适。
(但是有些书的作者认为不应该使用getClass检测,因为这样不符合置换原则)
3、《java核心技术卷》给出的编写一个完美equals方法的建议
(1)、显式参数命名为otherObject,稍后需要将它转换成另一个叫做other的变量。
(2)、检测this与otherObject是否引用同一个对象
if (this == otherObject) return true;
这条语句只是一个优化。实际上,这是一种经常采用的形式。因为计算这个等式要比一个个地比较类中的域所付出的代价要小的多。
(3)、检测otherObject是否为null,如果为null,返回false。这项检测是很有必要的。
if (otherObject == null) return false;
(4)、比较this与otherObject是否属于同一类。如果equals的语句在每个子类中有所改变,就用getClass检测:
if (getClass!== otherObject.getClass() ) return false;
如果所有的子类都拥有统一的语义,就使用instanceof检测:
if (! (otherObjec instanceof ClassName) return false;
(5)、将 otherObject 转换为相应的类类型变量:
ClassName other = (ClassName) otherObject;
(6)、现在开始对所有需要比较的域进行比较了。使用==比较基本类型域,使用equals比较对象域。如果所有的域都匹配,就返回true;否则返回false。
return field1==other.field1&&Objects.equals(field2, other.field2)
&& ...;
如果在子类重新定义equals,就要在其中包含调用 super.equals(other)。
4,使用集合时在重写equals的同时一定要重写hashCode,其他时候最好也一起重写以防止出现equals返回相同但是hashCode不同的特殊情况(这种情况多见于散列集合如Set)。
通常情况下两个元素的哈希码不同两个元素一定不同,两个元素的哈希码相同但元素可能不同。
当集合要添加新的元素或者查询时,会先调用这个元素的hashCode方法,来缩小查询范围从而提高代码效率。
5,重写hashCode时系数最好选择31的问题
31可以 由i*31== (i<<5)-1来表示,现在很多虚拟机里面都有做相关优化,使用31的原因可能是为了更好的分配hash地址,并且31只占用5bits!
在java乘法中如果数字相乘过大会导致溢出的问题,从而导致数据的丢失.
而31则是素数(质数)而且不是很长的数字,最终它被选择为相乘的系数的原因不过与此!
以上理解来自http://www.kuqin.com/algorithm/20120319/319123.html
也就是说31这个值是一个不大的奇素数,保证数据不会溢出的同时运算速度更快。