hashCode:哈希值

当用到Java中的集合时,会涉及到HashSet和HashMap,这里拿HashSet举例来说明关于hash的问题。

    当我们向HashSet集合中存入一个元素时,HashSet会调用该对象的的hashCode()方法来得到对象的hashCode值,然后根据hashCode值来决定对象的存放位置。如果两个对象使用equals方法比较结果为true,而hashCode值不相同,HashSet依然会接受这两个值,存放在集合中的不同位置。

    这也就意味着,如果想要在HashSet中不存在相同的元素,那么如果两个对象通过equals方法比较结果相等,那么两个对象的hashCode值也应该相等。

    如果两个对象的hashCode值相同,但是equals方法比较结果不相同,那么更加糟糕。HashSet试图将他们保存在同一个位置,但一个位置又只能保存一个,所以实际上会在这个位置使用链式结构来保存多个对象。因为HashSet是通过hashCode来定位元素的,这样的结构会导致性能下降。

    当我们想要重写对象的equals方法时,也因该重写hashCode()方法,以保证他们equals比较得到true时,hashCode值也相同。

    重写hash Code方法的基本规则:

        1:在程序运行过程中,同一个对象多次调用hashCode()方法应该返回相同的hashCode值。

        2:当两个对象通过equals方法比较结果为true时,来给你个对象的hashCode方法返回的值也应该相同。

        3:对象中用作equals方法比较标准的实例变量,都应该用于计算hashCode值。

    重写HashCode()方法的一般步骤:

        1:把对象的每个有意义的实例变量(即每个参于equals()方法比较的实例变量)计算出一个int类型的hashCode值。计算方式如下:

    实例变量类型:boolean   ------->计算方式:hashCode = (f?0:1);

    实例变量类型:整数类型(int、byte、short、char)   ------->计算方式:hashCode = (int)f;

    实例变量类型:long   ------->计算方式:hashCode = (int)(f^(f>>>32));

    实例变量类型:float   ------->计算方式:hashCode = Float.floatToIntBits(f);

    实例变量类型:double   ------->计算方式:Long l = Double.doubleToIntBits(f); hashCode = (int)(l^(l>>>32));

    实例变量类型:引用类型   ------->计算方式:hashCode = f.hashCode(f);

    2:用第一步计算出来的多个hashCode值组合计算出一个hashCode值返回。

        如:return f1.hashCode() +(int)f2;

        为了避免直接相加产生偶然相等,可以通过为各实例变量的hashCode值乘上一个质数后相加。

        如:return f1.hashCode() * 19 + (int)f2 * 31;

PS:当程序把可变对象添加到HashSet中后,尽量不要去修改该集合中怨怒是中参与计算hashCode、equals的实例变量,否则有可能导致Hashset无法正确操作这些元素。例:

import java.util.HashSet;
import java.util.Iterator;

class R{
	int count;
	public R(int count){
		this.count = count;
	}
	public String toString(){
		return "R[count:"+count+"]";
	}
	public boolean equals(Object obj){
		if(this == obj)
			return true;
		if(obj != null && obj.getClass() == R.class){
			R r = (R)obj;
			return this.count == r.count;
		}
		return false;
	}
	public int hashCode(){
		return this.count;
	}
}


public class HashSetTest2{
	public static void main(String[] args){
		HashSet hs = new HashSet();
		hs.add(new R(5));
		hs.add(new R(-3));
		hs.add(new R(9));
		hs.add(new R(-2));
		//打印HashSet集合,集合元素没有重复
		System.out.println(hs);
		//取出第一个元素
		Iterator it = hs.iterator();
		R first = (R)it.next();
		//为第一个元素的count实力变量赋值
		first.count = -3;
		//再次输出HashSet集合,集合元素有重复元素
		System.out.println(hs);
		//删除count为-3的R对象
		hs.remove(new R(-3));	//删除的是原本定义为-3的对象,而不是后面改动的对象。因为HashSet根据hashCode值定位。
		System.out.println(hs);
		System.out.println("hs是否包含count为-3的对象?"+hs.contains(new R(-3)));	//结果为false,因为被删除了
		System.out.println("hs是否包含count为-2的R对象?"+hs.contains(new R(-2)));	//结果为false,因为值被改动了
	}
	
}

猜你喜欢

转载自blog.csdn.net/wargon/article/details/80676041