理解hashcode

来源:互联网 发布:天天秒杀 淘宝智阳家纺 编辑:程序博客网 时间:2024/06/15 06:03

Java中,所有对象的父类----Object中,有一个方法:public int hashCode(),这个方法是做什么用的呢?通过eclipse重写某个类的equals方法时,一般eclipse也会自动帮你将hashCode()这个方法重写了,而且重写的hashCode()都是通过移位运算,得到一个int类型的数,这是为什么呢?

要解答这个疑问,最直接的办法是找到Object类的文档说明:

Returns a hash code value for the object. This method is supported for the benefit of hashtables such as those provided by java.util.Hashtable. 

文档说这个方法是为了提高哈希表的性能,为什么这个方法能提高哈希表的性能?继续往下看:

The general contract of hashCode is: •Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application. •If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result. •It is not required that if two objects are unequal according to the java.lang.Object.equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hashtables. 

上面约定的三点分别为:

1.只要对象内的属性没有发生变化(严格来说只要equals 比较时所用的信息没有被修改),则调用hashCode()应该返回固定的值。

2.如果两个对象执行equals方法比较返回true,则两个对象的hashCode()方法返回值也必须相同。

3.如果两个对象执行equals方法比较返回false,则两个对象的hashCode()方法返回值可相同,也可不同。

好像知道了这些约定的内容,还是不好理解为什么这个方法能提升哈希表的性能,再找到HashSet的源码来看一下。

在查看HashSet源码前,需要牢记的是HashSet集合里面存放的对象是不重复的,即HashSet内任意两个对象执行equals方法都会返回false。

那么,HashSet是如何保证你执行add方法添加进去的对象不重复呢?

public boolean add(E e) {return map.put(e, PRESENT)==null;    }

乍一看,为什么添加的对象成了一个map的key??好神奇!不过仔细一想,Map的key就是不重复的,这个设计还是很巧妙的!

上面代码中的map是HashMap<E,Object>,继续查看put代码:

public V put(K key, V value) {        if (key == null)            return putForNullKey(value);        int hash = hash(key.hashCode());        int i = indexFor(hash, table.length);        for (Entry<K,V> e = table[i]; e != null; e = e.next) {            Object k;            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {                V oldValue = e.value;                e.value = value;                e.recordAccess(this);                return oldValue;            }        }        modCount++;        addEntry(hash, key, value, i);        return null;    }

看到这里,要理解:要想保证元素都不重复,那每次添加新元素时,都需要将新添加进去的元素跟集合中现有的所有元素进行比较,都不同才添加到集合中。

但是,如果集合存放的元素已经很多了,这时如果只是单纯的执行equals方法,那么新加入的元素跟所有现存的元素都要进行一次equals比较,才能成功加入集合,而equals方法的效率不一定能达到要求。

这时候,hashCode()方法出场了!上面说到:equals()返回true则hashCode()一定相同,equals()返回false,hashCode()不一定相同,所以,换句话说,hashCode()返回值相同是两个元素equals()返回true的前提条件。

在看下面比较的代码,当hashCode()都不同时,是直接就跳过了执行equals()比较的,而两个int类型的数值比较肯定效率优于两个对象执行equals比较。

e.hash == hash && ((k = e.key) == key || key.equals(k))

所以,hashCode()就是通过这种巧妙的设计来“提高哈希表的性能”的。

1 0