您的位置:首页 > 编程语言 > Java开发

Java 从写equals 和 hashcode

2009-02-25 20:19 363 查看
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;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: