首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > JAVA > J2SE开发 >

请问hashCode的本质

2012-03-11 
请教hashCode的本质我想请问一下hashCode到底代表什么东西呢,对象的hashcode和内存地址有什么关系啊?Java

请教hashCode的本质
我想请问一下hashCode到底代表什么东西呢,对象的hashcode和内存地址有什么关系啊?

Java code
package wukun;     public class Stu {       private String name;       private String sex;              public Stu(String name, String sex) {           super();           this.name = name;           this.sex = sex;       }   }  package wukun;     import java.util.ArrayList;   import java.util.HashSet;   import java.util.Set;     public class HashCodeTest {              public static void main(String[] args) {              //      Set h1 = new HashSet();   //      Set h2 = new HashSet();   //         //      ArrayList a1 = new ArrayList();   //      ArrayList a2 = new ArrayList();   //         ////        a1.add(new String("abc"));     ////        a2.add(new String("abc"));   //         //      h1.add(a1);   //      h2.add(a2);   //         //      System.out.println(h1.hashCode());   //      System.out.println(h2.hashCode());   //                 Stu s1 = new Stu("wukun","男");           Stu s2 = new Stu("wukun","男");                      System.out.println(s1.hashCode());           System.out.println(s2.hashCode());                  }   } 为什么2个ArrayList的hash码一样,而2个自定义的stu对象的hash码就不一样呢?(主要问这个问题)






以下内容说明了为什么要重写equals和hashCode方法,供大家参考。
这是jdk1.5中hashmap中put方法的源代码,
Java code
public V put(K key, V value) {K k = maskNull(key);int hash = hash(k);int i = indexFor(hash, table.length);for (Entry<K,V> e = table; e != null; e = e.next) {if (e.hash == hash && eq(k, e.key)) {V oldValue = e.value;e.value = value;e.recordAccess(this);return oldValue;}}modCount++;addEntry(hash, k, value, i);return null;}

hashset的底层实际上是hashmap,通过一个entry数组来是实现的,数组的每一个元素又是一个链表,

eq(k, e.key)) 方法我看明白了,它的具体内容是这样的
Java code
static boolean eq(Object x, Object y) {return x == y || x.equals(y);}


这里的k是我们要存储的对象的引用,我错误的以为eq方法调用了object的equals方法,导致方法永远返回false,其实这里参数对象Object x虽然是Object的引用,但是它实际上指向我们外部一个实例化的子类,就是我们要往hashset中存的那个对象,这就很明显的告诉了我们为什么要重写equles方法了,当我们重写了equles方法以后,它就会去调用我们子类重写的那个方法,而不是调用Object的equels方法。
如果比较的结果相等,就代表hashset中已经存在了这个对象了,执行if语句里面的内容,这里的value好像没多大用处,它是一个没有内容的Object对象,但是当我们使用hashmap时,就是有value的,当保存一个key重复,但是value不重复的对象到hashmap中去的时候,这个key不变,而value被覆盖。

当for循环结束走到尽头时,表示hashset中没有这个对象,addEntry(hash, k, value, i);就会执行,它的源代码如下:
这个方法很好理解,它相当与把新内容插到了table【i】的头节点,原来的头节点变成了第2个。
Java code
void addEntry(int hash, K key, V value, int bucketIndex) {Entry<K,V> e = table[bucketIndex];table[bucketIndex] = new Entry<K,V>(hash, key, value, e);if (size++ >= threshold)resize(2 * table.length);}


resize(2 * table.length);相当扩充容量,这个就不用解释了,我也没细看。




[解决办法]
因为ArrayList的父类AbstractList重写了hashcode()方法,根据列表内容算hashcode,而你两个ArrayList里面放的字符串内容一致,
当然hashcode就一样啦!
至于你Stu类没重写hashcode()方法,所以使用的是Object类的hashcode()方法,默认输出对象地址,两个不同对象的地址当然不一致
[解决办法]
JDK规范里建议用对象在虚拟机中的地址作为hashCode计算出来的值,当然hash结构的本质意义是能更快的搜索到元素,这里也就是能帮助虚拟机更快的定位到虚拟机管理的对象,你的问题是:
为什么2个ArrayList的hash码一样,而2个自定义的stu对象的hash码就不一样呢?

因为ArrayList的hashCode方法被重写了,重写的代码在ArrayList的父类AbstractList里面,重写的逻辑大概是以元素的hashCode为值来计算,而你ArrayList里装的元素又都是"abc"这个String,虽然是两个内容相同的String对象,但String对象的hashCode方法也被重写了,以内容为原始值来就算出来的,所以简而言之,相等的原因是:



由于String类的hashCode以内容来计算,内容都是"abc",所以计算结果同,而ArrayList又以元素的hashCode来计算,而两个String元素的hashCode相等,所以计算结果也相等,最终导致两个ArrayList的hashCode也相等

而两个Stu对象hashCode不相等是因为它们的hashCode继承自Object父类,这个类的hashCode是一个native方法(C语言实现的),实现逻辑由虚拟机内部产生,综述你可以重写任何对象的hashCode来保证实现你需要的逻辑,而且很明显你已经知道为什么要重写hashCode方法了,这很好!

热点排行