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

哈希(Hash)相关---Java中的Hash机制(HashMap、HashSet及对其源码解析

2013-02-08 17:38 423 查看
-----------------equals()和hashCode()-------------

如何像把自定义的类和基于散列的集合类结构一起工作是,该类的equals()和hashCode()实现就显得尤为重要。

在改写equals方法的时候应该符合java.lang.Object的规范:

1.自反性:x.equals(x)一定为true;

2.对称性:由x.equals(y)为true,则必有y.equals(x)为true;

3.传递性:如果x.equals(y)为true并且y.equals(z)为true,则x.equals(z)为true必成立;

4.对于任意非空引用x,x.equals(null)一定返回false;

对应hashCode()应该符合java.lang.Object的规范:

1.在一个应用程序执行期间,如果一个对象的equals()作比较所用到的信息没有被修改的话,那么对象调用hashCode()

多次,它必须返回同一个整数。(在同一应用程序多次执行的过程中,这个整数可以不同。)

2.如果两个对象根据equals()方法相等,那么调用这两个对象中任一个对象hashCode()方法必须产生相同的整数结果。

3.如果两个对象根据equals()方法是不相等的,那么调用这两个对象的hashCode()方法,不要求必须产生不同的整数结

果。

一个散列函数应该把一个集合中不相等的实例均匀地分布到所有可能的散列值上。

一般的hashCode()可以这样按照如下的方法定义:

1.定义int result=17;(17,是一个某个非零的整数,可以根据需要任选)

2.对于对象中每一个关键域f(指equals方法中考虑到的每一个域),执行如下步骤:

a.为该域计算int类型的散列码c:

i.如果该域是boolean类型,则,计算(f?0:1);

ii.如果该域是byte、char、short或int类型,则计算(int)f

iii.如果该域是long类型,则计算(int)(f^(f>>>32));(其中>>>表示无符号右移位)

iv.如果该域是float类型,则计算Folat.floatToIntBits(f);

v.如果该域是double类型,则计算Double.doubleToLongBits(f)得到一个long类型的值,然后按照步骤iii对long类型数值计算散列值。

vi.如果该域是一个对象引用,并且该类的equals方法通过递归调用equals的方式来比较这个域, 则同样对这个域递归调用hashCode。

如果要求一个更为复杂的比较,则为这个域计算一个“规范表示”然后针对该范式表示调用hashCode,如果该域的值为null,则返回0.

vii.如果该域是一个数组,则把每一个元素当做单独的域来处理。也就是说,递归的应用上述规则 ,对每个重要的元素计算散列码,

然后根据b中的做法把这些散列值组合起来。

b.按照下面的公式,把步骤a中计算得到的散列码c组合到result中:

result=37*result+c;

3.返回result;

4.写完了hashCode方法之后,检测“是否相等的实例具有相等的散列码”,如果不是,查找修正。

在计算hashCode时应注意:在equals方法中没有被考虑到的域都要排除在外;域中的冗余值也可不用考虑;

------------------------------------------

注意:String对象有个特点:如果程序中有多个String对象,都包含相同的字符串序列,那么这些String对象都映射到同一块内存区域。所以,如String s1=new String("hello");String s2=new String("hello") ;两个对象实例虽然是独立的,但是对它们使用hashCode()应该生成同样的结果。对于String而言,hashCode明显基于String的内容。

--------------------------------------------

--------------java中经典Hash实现-HashMap和HashSet----(源码解析)-----------------------------------------

java.util.HashMap<K,V>主要是用数组来存储数据,采用链表的方式解决key散列后产生的冲突。

Java代码







public class HashMap<K,V>
extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable

{

/**
* 数组table默认初始容量,必须为2的幂,即2^p

*/
static final
int DEFAULT_INITIAL_CAPACITY = 16;

/**
* 数组table最大容量,必须为2^p<= 1<<30.
*/
static final
int MAXIMUM_CAPACITY = 1 <<
30;

/**
* 负载因子=可能的key的个数/存储容量

* 默认加载因子,加载因子是一个比例,当HashMap的数据大小>=容量*加载因子时,HashMap会将容量扩容

*/
static final
float DEFAULT_LOAD_FACTOR = 0.75f;

/**
* 这就是map的主要存储结构,其长度是2^p.
*/
transient Entry[] table;

/**
* 在map中存储的键值对的个数

*/
transient int size;

/**
* 当实际数据大小超过threshold时,HashMap会将容量扩容,threshold=容量*加载因子

*/
int threshold;

/**
* 负载因子

* @serial
*/
final float loadFactor;

/**
* modCount就是Map改变的次数,声明为volatile,保证线程之间修改的可见性;

*/
transient volatile
int modCount;

..,

public class HashMap<K,V>    extends AbstractMap<K,V>    implements Map<K,V>, Cloneable, Serializable
{

/**
* 数组table默认初始容量,必须为2的幂,即2^p

*/
static final int DEFAULT_INITIAL_CAPACITY = 16;

/**
* 数组table最大容量,必须为2^p<= 1<<30.
*/
static final int MAXIMUM_CAPACITY = 1 << 30;

/**
* 负载因子=可能的key的个数/存储容量

* 默认加载因子,加载因子是一个比例,当HashMap的数据大小>=容量*加载因子时,HashMap会将容量扩容

*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;

/**
* 这就是map的主要存储结构,其长度是2^p.
*/
transient Entry[] table;

/**
* 在map中存储的键值对的个数

*/
transient int size;

/**
* 当实际数据大小超过threshold时,HashMap会将容量扩容,threshold=容量*加载因子

*/
int threshold;

/**
* 负载因子

* @serial
*/
final float loadFactor;

/**
* modCount就是Map改变的次数,声明为volatile,保证线程之间修改的可见性;

*/
transient volatile int modCount;

..,
-------------
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: