我想请问一下hashCode到底代表什么东西呢,对象的hashcode和内存地址有什么关系啊?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方法的源代码,
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)) 方法我看明白了,它的具体内容是这样的
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个。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);相当扩充容量,这个就不用解释了,我也没细看。

解决方案 »

  1.   

    此回复为自动发出,仅用于显示而已,并无任何其他特殊作用
    楼主【wnba1983】截止到2008-07-21 20:42:29的历史汇总数据(不包括此帖):
    发帖的总数量:56                       发帖的总分数:1670                     每贴平均分数:29                       
    回帖的总数量:54                       得分贴总数量:5                        回帖的得分率:9%                       
    结贴的总数量:29                       结贴的总分数:750                      
    无满意结贴数:1                        无满意结贴分:20                       
    未结的帖子数:27                       未结的总分数:920                      
    结贴的百分比:51.79 %               结分的百分比:44.91 %                  
    无满意结贴率:3.45  %               无满意结分率:2.67  %                  
    楼主加油
      

  2.   

    hashCode是标示一个对象的唯一身份,无他
      

  3.   

    就是一个散列码。一般情况下,如果hashCode相同,则equals应该也判定相等。就像MD5一样,但没MD5那么复杂。
      

  4.   

    因为ArrayList的父类AbstractList重写了hashcode()方法,根据列表内容算hashcode,而你两个ArrayList里面放的字符串内容一致,
    当然hashcode就一样啦!
    至于你Stu类没重写hashcode()方法,所以使用的是Object类的hashcode()方法,默认输出对象地址,两个不同对象的地址当然不一致
      

  5.   

    相同的字符串hash的结果肯定是一样,而你后面是用对象地址去hash的,原因楼上讲了.
      

  6.   

    也就是说,只要对象内容一样,我们有就必要重写它的hashCode,hashCode代表的是对象的内存地址,或者说内存地址根据这个hash码得到,
    一般的集合都重写了hashCode方法,而当我们自定义对象的时候就要重写hasdCode方法,保证内容一样的对象算出相同的hash码hash码的用处只体现在需要散列的地方,入hashSet、hashMap,用户查找速度,其他地方它没有什么用,对吧呵呵,有种豁然开朗的感觉,谢谢你了
      

  7.   

    hashCode既散列码 每个对象的散列码是通过该对象的内存地址来分配的
    则2个对象equals则起散列码也必须相同
    所以重写equals方法必须重写hashCode方法
    重写hashCode方法的目的就是让2个对象散列码相同
      

  8.   

    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方法了,这很好!
      

  9.   

    主要是因为String类重写了equals方法,重写equals方法就应该重写hashCode()方法,你定义的Stu是个一般的类,它的equals方法还是Object中的equals方法,hasCode方法也还是Object中的hasCode方法,所以s1跟s2的hashCode不等
      

  10.   

    2楼的说法,应该不对,hashCode不能唯一标识一个对象,内存中会出现hashCode相同但物理地址不同的两个对象.
      

  11.   


    每一个对象的hashCode 本身是不一样的!除非你重写 hashCode 方法!2个ArrayList的hash码一样,是因为 ArrayList 有重写hashCode 方法而且 你的ArrayList里面放置的对象 也重写了 hashCode方法! 是一样很正常!而2个自定义的stu对象的hash码就不一样呢?回答:每一个对象的hashCode 本身是不一样的!
      

  12.   

    谈一谈HashSet中hashCode的作用.
    说一下我自己的理解.在存放集合,这种数据的时候,我们可以选择List和Set两种形式,当然,Java当中它们不是具体的实现类.我们可以使用具体的实现类进行数据的存储.
    但是List和Set的最显著区别,应该是,List可以放置相同的元素,Set只能放置不同的元素.也就是说Set里面的元素具有唯一性.当然ArrayList和LinkedList也只是具体的实现形式不同了.我们也可以实现一个ArraySet或LinkedSet.但是.我们会发现一个比较严重的问题.就是.当我们向ArraySet(或LinkedSet)里面添加元素的时候,我们要逐个比较里面的所有已有元素.这样,当集合中数量非常多的时候,比较次数也会直线上升.也就是说,当我们结合中有10000个元素的时候,我再添加一个元素,要首先比较最多10000次才能确定我要添加的元素是否已经存在了,这会严重影响集合的性能.为了更快捷的检索数据,我们才引进了hashCode的概念,每个Set里面的元素都会有一个hashCode的值,我们可以按照hashCode的值来存储集合里面的元素.如果要检索集合里面是否存在要添加的元素,只要先将该元素的hashCode值算出来,再到相应的位置进行查找,就可以了.对于相同hashCode的不同元素,我们把这个位置,按照链表的形式进行存放.这样,就可以很大程度上减少比较的次数.举个例子:我们Set集合里面可能已经10000个不同的元素了.当添加新元素的时候,我们根据新元素的hashCode值,找到相应的位置,这个位置所对应的链表里面,可能只有五个元素,那么,我们只要比较5次,就可以判断整个集合中是否已经存在该元素了,因为,相同元素的hashCode一定是相同的.呵呵.为了使根据hashCode确定位置的速度更快,我们采用数组的下标来表示位置,数组里面存放链表.下标都是自然数,所以,要把hashCode进行向数组下标的映射转换(其实就是与运算).最后,来谈谈,什么情况下重写hashCode,我们知道,自然情况下,hashCode所产生的值是很有规律的,这样的话,拥有10000个元素的Set,可能有9000个都在同一个位置上,这样,再加一个相同hashCode的元素时,那可能要比较90000次了.所以,我们要自己定义hashCode使得这些元素的hashCode在hashSet里面存储更加分散.不过Java的HashSet里面,已经有一个方法,将元素的hashCode做打散处理了,这个方法对于默认的hashCode还是比较有效果的.对于熟悉这些内容的程序员,为了程序的效率更高,可以重写hashCode方法.但是,不能使用随机数充当hashCode,每个元素,都应该对应唯一的一个hashCode,一个hashCode可以对应多个元素.
      

  13.   

    Equals & Hash Code relationship -  The last note from the API documentation is very important, it states the relationship requirement between these two methods. It simply means that if two objects are equal, then they must have the same hash code, however the opposite is NOT true.
      

  14.   

    14楼的说明很好,对象的 hashCode 相当于图书的索引,可以帮助我们快速地查找和定位集合中的元素。我对象的 hashCode 方法默认(如果此对象所属的类并没有覆盖从 Object 继承下来的 hashCode 方法)返回对象的内存地址,当然不同对象所占用的内存地址肯定不同,符合 hashCode 的最基本要求,但为了使 hashCode 真正发挥出它的作用,一般要精心的设计一下自己类的 hashCode 方法。
      

  15.   


    讲得有点晕了.
    第四段:.对于相同hashCode的不同元素,我们把这个位置,
    在Set里,会存在相同hashCode的不同元素?Set里元素的值不是不能重复么?会有相同的hashCode?
      

  16.   

    Set里的元素当然不能重复,但是,这些元素的hashCode是有可能相同的.这个18楼可以自己写一个类,重写hashCode方法,进行测试.
      

  17.   


    是不是可以这样理解: 在set.add()之前,会把要插入的值的hashcode计算出来,再到一段区域去找有没有equals的对吧?  意思就是不同的hashcode()是不可能会有相同的值对不对?还有一个问题:传说hash的查找速度是最快的就是根据hashcode去找到一段可能会有相等值的地方。 为什么能这样定位到那块需要查找的地方?  而在其它地方不会出现相同的值。   不太理解这一块。。哪位大虾讲下。不甚感激。。想了一天也想不通。   查找机制。
      

  18.   

    类似实现的一段/** Returns true if the String is in the set */  
      private int getSlot(CharSequence text) {
        int code = getHashCode(text);
        int pos = code & (entries.length-1);
        char[] text2 = entries[pos];
        if (text2 != null && !equals(text, text2)) {
          final int inc = ((code>>8)+code)|1;
          do {
            code += inc;
            pos = code & (entries.length-1);
            text2 = entries[pos];
          } while (text2 != null && !equals(text, text2));
        }
        return pos;
      }
      

  19.   

    lz 问题就在这里
    ////        a1.add(new String("abc"));     
    ////        a2.add(new String("abc"));   
    如果把第二个String对象换成不同的 比如“ab”,那结果哈希码就不一样了。