我想请问一下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方法也被重写了,以内容为原始值来就算出来的,所以简而言之,相等的原因是: