
2007年12月04日 12:49:47
Java基础之 与equals()如影随行的hashCode()
|
上一篇文章我们谈到了equals()的重写,接下来我们说说与equals()关系紧密的hashCode(),这从标题就可窥见一斑。 public class ColorPoint{ private Point point; private Color color; public ColorPoint(int x, int y, Color color){ point = new Point(x, y); this.color = color; } //返回一个与该有色点在同一位置上的普通Point对象 public Point asPoint(){ return point; } public boolean equals(Object o){ if(o == this) return true; if(!(o instanceof ColorPoint)) return false; ColorPoint cp = (ColorPoint)o; return cp.point.equals(point)&& cp.color.equals(color); } } 假设你试图将这个类与HashMap一起使用: public class Test{ public static void main(String[] args){ Map m = new HashMap(); m.put(new ColorPoint(1,2,Color.RED),"red"); System.out.println(m.get( new ColorPoint(1,2,Color.RED))); } } 这时候,你可能会期望程序返回并输出"red",但是实际的运行结果是:null。为什么呢?因为这里涉及到两个ColorPoint实例:第一个被用于插入到HashMap中,第二个实例与第一个相等,被用于(试图)检索。由于ColorPoint类没有重写hashCode方法,从而导致两个相等的实例具有不相等的散列码,违反了hashCode的约定。因此,要想解决这个问题,只需为ColorPoint类提供一个适当的hashCode方法既可。 如何编写一个适当的hashCode方法呢?下面是我从《Effective Java》一书中摘抄而来的“诀窍”: 1.把某个非零常数值,比如说17,保存在一个int类型的变量result中。 2.对于对象中每个关键域f(指equals方法中考虑的每个域),完成以下步骤: A.为该域计算int类型的散列码c: a.如果该域是boolean类型,则计算(f ? 0 : 1)。 b.如果该域是byte、char、short或int类型,则计算(int)f。 c.如果该域是long类型,则计算(int)(f ^ (f >>> 32))。 d.如果该域是float类型,则计算Float.floatToIntBits(f)。 e.如果该域是double类型,则计算Double.doubleToLongBits(f)得到一个long类型的值,然后按照步骤2.A.c ,对该long型值计算散列值。 f.如果该域是一个对象引用,并且该类的equals方法通过递归调用equals的方式来比较这个域,则同样对该域递归调用hashCode。 g.如果该域是一个数组,则把每一个元素当做一个单独的域来处理。也就是说,递归地应用上述规则,对每个重要的元素计算散列值。 B.按照下面的公式,把步骤A中计算得到的散列码c组合到result中: result = 37*result + c; 3.返回result。 4.写完了hashCode方法之后,问自己“是否相等的实例具有相等的散列码”。如果不是的话,请找出原因,并修正错误。 现在,我们把这种方法用到ColorPoint类中。它有两个关键域,都是对象引用类型。根据上面的步骤,很直接地会得到下面的散列函数: public int hashCode(){ int result = 17; result = 37*result + point.hashCode(); result = 37*result + color.hashCode(); return result; } 接下来还要为Point类重写hashCode方法,它的两个关键域都是int类型,添加如下代码: public int hashCode(){ int result = 17; result = 37*result + x; result = 37*result + y; return result; } 最后,我们再次运行上面的Test类,看看实际的运行结果是否和我们期望的值一样。亲爱的朋友们,你们的运行结果怎样?请用留言的方式来告诉我。 |
一共有 1 条评论
http://hi.baidu.com/sio2007