Java 从写equals 和 hashcode


1.何时需要重写equals()
 在前面容器的笔记中已经提到过,在回顾一下,当我们需要逻辑相等的判段时,我们必须重写,因为Object中的方法当且仅当为同一个对象时才返回true,这不是我们想要的。
2.设计equals()
[1]使用instanceof操作符检查“实参是否为正确的类型”。
[2]对于类中的每一个“关键域”,检查实参中的域与当前对象中对应的域值。
 [1]对于非float和double类型的原语类型域,使用==比较;
 [2]对于对象引用域,递归调用equals方法;
 [3]对于float域,使用Float.floatToIntBits(afloat)转换为int,再使用==比较;
 [4]对于double域,使用Double.doubleToLongBits(adouble) 转换为int,再使用==比较;
 [5]对于数组域,调用Arrays.equals方法。
3.当改写equals()的时候,总是要改写hashCode()
 根据一个类的equals方法(改写后),两个截然不同的实例有可能在逻辑上是相等的,但是,根据Object.hashCode方法,它们仅仅是两个对象。因此,违反了“相等的对象必须具有相等的散列码”。
4.设计hashCode()
从实现来说,一般的HashCode方法会这样:
   return Attribute1.HashCode() + Attribute1.HashCode()..[+super.HashCode()]。
   我们知道,每次调用这个方法,都要重新对方法内的参与散列的对象重新计算一次它们的HashCode的运算,如果一个对象的属性没有改变,仍然要每次都进行计算,所以如果设置一个标记来缓存当前的散列码,只要当参与散列的对象改变时才重新计算,否则调用缓存的hashCode,这可以从很大程度上提高性能。
   默认的实现是将对象内部地址转化为整数作为HashCode,这当然能保证每个对象具有不同的HasCode,因为不同的对象内部地址肯定不同(废话),但java语言并不能让程序员获取对象内部地址,所以,让每个对象产生不同的HashCode有着很多可研究的技术。
   如果从多个属性中采样出能具有平均分布的hashCode的属性,这是一个性能和多样性相矛盾的地方,如果所有属性都参与散列,当然hashCode的多样性将大大提高,但牺牲了性能,而如果只能少量的属性采样散列,极端情况会产生大量的散列冲突,如对"人"的属性中,如果用性别而不是姓名或出生日期,那将只有两个或几个可选的hashcode值,将产生一半以上的散列冲突.所以如果可能的条件下,专门产生一个序列用来生成HashCode将是一个好的选择(当然产生序列的性能要比所有属性参与散列的性能高的情况下才行,否则还不如直接用所有属性散列)。
     下面是个不错的方法:
    [1]把某个非零常数值,例如17,保存在int变量result中;
    [2]对于对象中每一个关键域f(指equals方法中考虑的每一个域):
    [1]boolean型,计算(f ? 0 : 1);
    [2]byte,char,short型,计算(int);
    [3]long型,计算(int) (f ^ (f>>>32));
    [4]float型,计算Float.floatToIntBits(afloat);
    [5]double型,计算Double.doubleToLongBits(adouble)得到一个long,再执行[2.3];
    [6]对象引用,递归调用它的hashCode方法;
    [7]数组域,对其中每个元素调用它的hashCode方法。
[3]将上面计算得到的散列码保存到int变量c,然后执行 result=37*result+c;
[4]返回result。
5.示例
 下面的这个类遵循上面的设计原则,重写了类的equals()和hashCode()。
package com.zj.unit;
import java.util.Arrays;
 
public class Unit {
   private short ashort;
   private char achar;
   private byte abyte;
   private boolean abool;
   private long along;
   private float afloat;
   private double adouble;
   private UnitaObject;
   private int[]ints;
   private Unit[]units;
 
   public boolean equals(Object o) {
    /**
     *不推荐使用,如果是有继承关系的类,就不好说了,哈哈,自己想啊!多态。
     * if (!(oinstanceof Unit))
     *    return false;
     */
      if(this == o)
             return true;
      if(o == null || !o.getClass().equals(this.getClass()))
             return false;
      Unit unit = (Unit) o;
      return unit.ashort ==ashort
             && unit.achar ==achar
             && unit.abyte ==abyte
             && unit.abool ==abool
             && unit.along ==along
             && Float.floatToIntBits(unit.afloat) == Float
                    .floatToIntBits(afloat)
             && Double.doubleToLongBits(unit.adouble) == Double
                    .doubleToLongBits(adouble)
             && unit.aObject.equals(aObject)
 && equalsInts(unit.ints)
             && equalsUnits(unit.units);
   }
 
   private boolean equalsInts(int[] aints) {
      return Arrays.equals(ints, aints);
   }
 
   private boolean equalsUnits(Unit[] aUnits) {
      return Arrays.equals(units, aUnits);
   }
 
   public int hashCode() {
      int result = 17;
      result = 37 * result + (int)ashort;
      result = 37 * result + (int)achar;
      result = 37 * result + (int)abyte;
      result = 37 * result + (abool ? 0 : 1);
      result = 37 * result + (int) (along ^ (along >>> 32));
      result = 37 * result + Float.floatToIntBits(afloat);
      long tolong = Double.doubleToLongBits(adouble);
      result = 37 * result + (int) (tolong ^ (tolong >>> 32));
      result = 37 * result +aObject.hashCode();
      result = 37 * result + intsHashCode(ints);
      result = 37 * result + unitsHashCode(units);
      return result;
   }
 
   private int intsHashCode(int[] aints) {
      int result = 17;
      for (int i = 0; i < aints.length; i++)
          result = 37 * result + aints[i];
      return result;
   }
 
   private int unitsHashCode(Unit[] aUnits) {
      int result = 17;
      for (int i = 0; i < aUnits.length; i++)
          result = 37 * result + aUnits[i].hashCode();
      return result;
   }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值