含义
equal和hashCode都是Object类中的方法
public boolean equals(Object obj) { return (this == obj); } public native int hashCode();
equals默认是比较对象的指针是否指向同样的内存地址。
hashCode是本地方法 : 会根据内存地址转换而来。
重写equals和hashCode原则: equals一样,则hashCode也必须一致
这个到底很容易想通,对于使用hash散列的数据结构如hashMap,首先会根据对象的hashCode找到桶的位置,再在列表或红黑树中用equals比较对象来判断对象是否已经存在
如果同一个对象,得到的hasCode不一致,则可以在hashMap中存放到多个桶中,导致内存泄漏。
equals是比较对象的地址,如果我们判断两个对象时根据内部某些属性来的话,就要重写equals和hashCode
重写equals和hashCode
原则:
-
自反性。对于任何非null的引用值x,x.equals(x)应返回true。
-
对称性。对于任何非null的引用值x与y,当且仅当:y.equals(x)返回true时,x.equals(y)才返回true。
-
传递性。对于任何非null的引用值x、y与z,如果y.equals(x)返回true,y.equals(z)返回true,那么x.equals(z)也应返回true。
-
一致性。对于任何非null的引用值x与y,假设对象上equals比较中的信息没有被修改,则多次调用x.equals(y)始终返回true或者始终返回false。
-
对于任何非空引用值x,x.equal(null)应返回false。
我们看IDEA为我们生成的例子
@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Attr1 attr1 = (Attr1) o; return name.equals(attr1.name) && area.equals(attr1.area) && sub.equals(attr1.sub); } @Override public int hashCode() { return Objects.hash(name, area, sub); }
equals重写
1、优先判断地址是否一样,如果一致,则必然是同一个对象,自然相等。
2、在判断类型是否一致 这里一般有两种选择 getClass或instanceOf操作
3、强制转换后 判断某些域是否相等
对于第2点,我们可以看看差别
public class Test { public static void testInstanceof(Object x) { System.out.println("x instanceof Parent: "+(x instanceof Parent)); System.out.println("x instanceof Child: "+(x instanceof Child)); System.out.println("x getClass Parent: "+(x.getClass() == Parent.class)); System.out.println("x getClass Child: "+(x.getClass() == Child.class)); } public static void main(String[] args) { testInstanceof(new Parent()); System.out.println("---------------------------"); testInstanceof(new Child()); } } class Parent { } class Child extends Parent { } /* 输出: x instanceof Parent: true x instanceof Child: false x getClass Parent: true x getClass Child: false --------------------------- x instanceof Parent: true x instanceof Child: true x getClass Parent: false x getClass Child: true */
getClass是严格判断类似是否一致。父子类getClass也是不等的
instanceOf是判断是否为某个类型的实例,是包含子类的。
前面我们说到equal的几个原则,其中就有对称性
假设有父子类Father 和 Son ,Father使用instanceOf来重写equals,有father.equals(son)==true;
此时必然要求son.equals(father)==true也成立,想想我们能保证吗,假设son使用getClass也重写了equals呢。
所以一般情况下,建议使用getClass来严格判断类型是否一致。
hashCode重写
hashCode需要使用那些用于判断equals的属性 来重写计算hashCode.
我们看看Objects.equals方法最终会调用如下方法:
public static int hashCode(Object a[]) { if (a == null) return 0; int result = 1; for (Object element : a) result = 31 * result + (element == null ? 0 : element.hashCode()); return result; }
我们再看看String的hashCode
public int hashCode() { int h = hash; if (h == 0 && value.length > 0) { char val[] = value; for (int i = 0; i < value.length; i++) { h = 31 * h + val[i]; } hash = h; } return h; }
注意没有,都会乘以31这个数字,为啥
effective java有如下2点解释
1、31是一个奇质数
超过 50,000 个英文单词进行 hash code 运算,并使用常数 31, 33, 37, 39 和 41 作为乘子,每个常数算出的哈希值冲突数都小于7个(大神做的测试),可见质数能有效降低
hash冲突率,2也是一个质数,如果用2作为因子,所得的数值区间比较小;hashCode冲突的可能会增加,如果用大的数如何101,hashCode计算的结果超过int类型的取值范围
概率又增加,导致数据溢出,脱落程序控制。
2、x*31 的运行,JVM可优化通过位运算和减法运算获取 x*31 = (x << 5) -x ,CPU可以高效运行
参考引用:
https://blog.csdn.net/hzw19920329/article/details/51095413