含义
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
- */
- View Code
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 可以高效运行
参考引用:
来源: http://www.bubuko.com/infodetail-3164018.html