Java基础之 与equals()如影随行的hashCode()
2008-10-11 12:49
429 查看
Java基础之 与equals()如影随行的hashCode()
上一篇文章我们谈到了equals()的重写,接下来我们说说与equals()关系紧密的hashCode(),这从标题就可窥见一斑。hashCode()返回该对象的哈希码值,该值通常是一个由该对象的内部地址转换而来的整数,它的实现主要是为了提高哈希表(例如java.util.Hashtable提供的哈希表)的性能。在每个重写了equals方法的类中,你必须也要重写hashCode方法。如果不这样做的话,就会违反Object.hashCode的通用约定,从而导致该类无法与所有基于散列值(hash)的集合类结合在一起正常运行。
下面是hashCode约定的内容,来自java.lang.Object的规范:
l 在一个Java应用程序执行期间,如果一个对象的equals方法做比较所用到的信息没有被修改的话,那么,对该对象调用hashCode方法多次,它必须始终如一地返回同一个整数。在同一个应用程序的多次执行过程中,这个整数可以不同。
l 如果两个对象根据equals(Object)方法是相等的,那么对这两个对象中的每一个对象调用hashCode方法都必须生成相同的整数结果。
l 如果两个对象根据equals(Object)方法是不相等的,那么调用这两个对象中任一个对象的hashCode方法,不要求必须产生不同的整数结果。然而,程序员应该意识到这样的事实,对于不相等的对象产生截然不同的整数结果,有可能提高散列表的性能。
下面我们仍然用上一篇文章“(override)重写equals()方法”中所用的例子,来详细解释上面约定的内容:
[align=left]public class ColorPoint{ [/align][align=left]
private Point point; [/align][align=left]
private Color color; [/align][align=left]
public ColorPoint(int x, int y, Color color){ [/align][align=left]
point = new Point(x, y); [/align][align=left]
this.color = color; [/align][align=left]
} [/align][align=left]
//返回一个与该有色点在同一位置上的普通Point对象 [/align][align=left]
public Point asPoint(){ [/align][align=left]
return point; [/align][align=left]
} [/align][align=left]
public boolean equals(Object o){ [/align][align=left]
if(o == this) [/align][align=left]
return true; [/align][align=left]
if(!(o instanceof ColorPoint)) [/align][align=left]
return false; [/align][align=left]
ColorPoint cp = (ColorPoint)o; [/align][align=left]
return cp.point.equals(point)&&
[/align][align=left] cp.color.equals(color); [/align][align=left]
} [/align][align=left]
} [/align]
假设你试图将这个类与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类,看看实际的运行结果是否和我们期望的值一样。
相关文章推荐
- java基础:equals() 和 hashCode()的一些理解
- java基础-hashCode()和equals()的本质区别和联系
- 【Java基础之重写equals、hashCode和compareTo方法】什么时候需要重写重写equals方法?为什么重写equals方法,一定要重写HashCode方法?
- 【Java基础之Object类(一)】Java中Object类中的所有方法(toString、equals、hashCode、clone、finalize、wait和notify等)详解
- Java基础复习(一)------equals、==和hashCode
- java基础面试知识点---------java中==和equals和hashCode的区别
- Java基础: HashSet 与 hashCode、equals
- Java基础 hashCode() 和equals() 区别和作用
- Java基础知识——equals方法和HashCode方法介绍
- java基础入门-hashcode与equals方法
- Java基础:hashCode与equals个人学习记录
- java基础学习总结——Java hashCode() equals()总结
- java基础----比较对象 hashcode 与 equals 与 ==
- Java基础之hashCode()的作用,以及==、equals()和hashCode()区别
- java基础入门-hashcode与equals方法
- 【12】Java基础: equals 与 == 的关系,equals 与 hashCode 的关系
- [java基础]==、equals和hashCode()
- Java基础重温(七)java中hashcode()和equals(),equals()与==
- Java基础[5]-1-this和super;静态和动态绑定;equals与hashCode和toString
- java基础入门-hashcode与equals方法