资料不少,多是照本宣科,读起来总觉的很生涩, 始终不能理解, 请高人用通俗的语言描述一下:
先谢谢了

解决方案 »

  1.   

    hashcode()和equals()都可以用于判断两个对象是否相等。hashcode()有一个使用协定,描述:
         在 Java 应用程序执行期间,在同一对象上多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是对象上 equals 比较中所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。 
         如果根据 equals(Object) 方法,两个对象是相等的,那么在两个对象中的每个对象上调用 hashCode 方法都必须生成相同的整数结果。 
         以下情况不 是必需的:如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么在两个对象中的任一对象上调用 hashCode 方法必定会生成不同的整数结果。但是,程序员应该知道,为不相等的对象生成不同整数结果可以提高哈希表的性能。 感觉这个描述的够清楚了。
      

  2.   

    equals是比较两个对象的内容是否相等的hashcode方法主要用在集合框架中,目的是为了快速比较两个对象是否相等,因为集合框架中的对象很多,每个都使用equals比较小了很差。
    每个对象都有一个hashcode,规定:
    1、内容相同的对象hashcode肯定相等
    2、内容不相同的对象hashcode可能相等也可能不相等所以如果两个对象的hashcode不相等则两个对象的内容肯定不相等,这样就不必一个一个去比较属性的值了,从而提高对象比较的速度。可以参看我BLOG中的《Java编程那些事儿》中关于Object类的介绍。
      

  3.   

    在Java中,equals()和hashCode()两个函数配合使用的.hashCode()所返回的是整型值,是用来分类对象在一些特定的收集对象中的位置,比如HashMap, Hashtable, HashSet等.在应用中,我们比较两个对象是否“相等”,过程主要是这样的:先比较两个对象的哈希码(当然是由对象的hashCode()方法得到)是否相等,如果哈希码不相等则我们就认为两个对象“不相等(注意这不是我们一般意义上的不相等,说白了就是不重复)”;而如果哈希码相等,则还要用equals()方法进行进一步判断:如果equals()比较的结果为true,则认为两个对象“相等(这是彻底相等了)”,如果equals()方法比较的结果为false,则认为两个对象“不相等(即认为不重复)”。这个过程在HashSet里插入数据时表现的淋漓尽致。
      

  4.   

    一般应用中你不需要了解hashcode用到集合框架的时候你需要了解,比如说你用hashmap当你用object的key来取hashmap的value的时候,hashmap是这样工作的:通过你传入的object的key去内存中找地址,当找到这个地址后在通过equals来比较这个地址中的内容是不是和你原来放进去的时候是一样的,是一样的就取出value(因为一个地址中可能会对应好几个内容,在hashcode没有被重写之前)。
    但是如果你new一个object作为key比如说说你Object o=new Object();o.hashcode()(其实这个就是我们说的那个key);这样我们用key去拿value的时候是拿不到的因为没new一次都可能得到一个不同的hashcode。所以我们要重写hashcode()这个方法,让new的时候这个hashcode不变。这样你永远可以通过这个key取到value。然后你要重写你的equals()方法,使内存中的内容也相等
      

  5.   

    建议读一下机械工业出版社的《Java高效编程》,讲得很清楚。
      

  6.   

    1.前提是对象上 equals 比较中所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。这句不懂哈, “对象上 equals 比较中所用的信息没有被修改”?
                "从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。" ,该整数是指?
               为什么呢?
      

  7.   

    指hashcode值呀,比如应用A的hashcode值为8,那么同一个对象,换作另一个应用就有可能是10,可能是生成规则有变化吧,就像不同系统的jvm一样
      

  8.   


    谢谢回答啊, 你的话是否可以这样理解:
         1. 比较两个对象是否相等可以用equals(),也可以用hashcode(), 但后者的效率高
                 假设有两个对象a, b;
                 equals()的比较方式是:
                         a.equals(b), 如果结果为true则a,b内容相等;为false则不等
               ---
               hashcode()的比较方式是否是:
                         a.hashcode() == b.hashcode(),  如果结果为true则a,b内容可能相等也可能不相等;为false则一定不等
                         那是否意味着用hoashcode()比较对象内容不彻底?,既只能判定内容不等的对象,但是对于hashcode相等的对象,则判定不了??
         2,每个对象都有一个hashcode,但这个hashcode是何时产生的,保存在哪里, 比如:是在把一个对象放到集合中(是任意一个集合吗)时,由集合自动产生,然手作为对象的内建属性保存起来,还是只在通过显式调用对象的hashcode()方法时才产生这个编码?     3, hashcode主要用在集合中用于快速比较对象是否相等,这句话如何理解呢,是比较两个集合对象是否相等(因为比较集合相等需要比较集合中的对象是否相等)还是其它的?    4,“内容相同的对象hashcode肯定相等 ,内容不相同的对象hashcode可能相等也可能不相等 ” 这个是约定,如何理解“约定” 这个词呢,我的理解是既然是约定,可以遵照也可以不遵照, 如果不按照约定又会出现什么情况呢?
    先再谢谢啦     
      

  9.   

    6楼的朋友解决了我的这个疑问
    -----------
     比较两个对象是否相等可以用equals(),也可以用hashcode(), 但后者的效率高 
                假设有两个对象a, b; 
                equals()的比较方式是: 
                        a.equals(b), 如果结果为true则a,b内容相等;为false则不等 
              --- 
              hashcode()的比较方式是否是: 
                        a.hashcode() == b.hashcode(),  如果结果为true则a,b内容可能相等也可能不相等;为false则一定不等 
                        那是否意味着用hoashcode()比较对象内容不彻底?,既只能判定内容不等的对象,但是对于hashcode相等的对象,则判定不了?? 
    --------------比较两个对象是否相等
    先比较两个对象的哈希码(当然是由对象的hashCode()方法得到)是否相等,如果哈希码不相等则我们就认为两个对象“不相等(注意这不是我们一般意义上的不相等,说白了就是不重复)”;而如果哈希码相等,则还要用equals()方法进行进一步判断:如果equals()比较的结果为true,则认为两个对象“相等(这是彻底相等了)”,如果equals()方法比较的结果为false,则认为两个对象“不相等(即认为不重复)”谢谢你们啦
      

  10.   


    觉得这位朋友说的很在理,  有几点想不通:
    1.“hashmap是这样工作的:通过你传入的object的key去内存中找地址,当找到这个地址后在通过equals来比较这个地址中的内容是不是和你原来放进去的时候是一样的”
       通过传入的object的key去内存中找地址,  这个地址是键的地址还是値的地址? 
       找到内存中的地址后,其内容可以获取,但如何记录原来放进去的key所对应的内容呢,它是通过哪种方式实现的,结构图是什么呢?”
    2.“(因为一个地址中可能会对应好几个内容,在hashcode没有被重写之前)。” 
        你说的这个地址是指内存地址还是其它什么地址?  如果是内存地址,一个地址可以对应几个内容该如何理解呢
    3. “但是如果你new一个object作为key比如说说你Object o=new Object();o.hashcode()(其实这个就是我们说的那个key);这样我们用key去拿value的时候是拿不到的因为没new一次都可能得到一个不同的hashcode。所以我们要重写hashcode()这个方法,让new的时候这个hashcode不变。这样你永远可以通过这个key取到value。然后你要重写你的equals()方法,使内存中的内容也相等”
        对该段不是太理解喔,汗, 你的意识是否是: 如果hashmap中存在一个Object类型的 key, 之后再new了一个Object对象出来, 这时的hashcode已经改变,进而取不到hashmap中的Object key所对应的値;  
        解决以上问题的方式是,在new Object()对象的时候,重写其hashcode()方法,让new的时候这个hashcode不变,为什么要再重写equals()方法呢?
      

  11.   

      解决以上问题的方式是,在new Object()对象的时候,重写其hashcode()方法,让new的时候这个hashcode不变,为什么要再重写equals()方法呢?
    看一下Object下equals的源代码:
      public boolean equals(Object obj) {
    return (this == obj);
        }
    也就是说在不重写equals()的情况下它比较的是对象是否相等比不是比较值是不是相等,所以如果我们不重写的话,如下面这样 
    Object p1=new Object();
    Object p2=new Object();
    p1和p2都是Object的引用也就是说p1.valuse=p2.valuse,但是如果不重写equals()的时候p1!=p2->p1.valuse!=p2.valuse。所以我们把它重写,让他不是比较对象是否相等而是比较值是不是相等。
      

  12.   

    看到一则介绍,重载equals时永远要重载hashCode 
    一定要在每一个重载了equals的类中重载hashCode方法。不这样做会违背Object.hashCode的一般约定,并导致你的类与所有基于散列的集合一起作用时不能正常工作,这些集合包括HashMap、HashSet和Hashtable。 不重载hashCode方法违背了java.lang.Object的规范:相等的对象必须有相等的散列码。两个截然不同的实例根据类的equals方法也许逻辑上是等同的,但对于Object类的hashCode方法,它们就是两个对象,仅此而已。因而对象的hashCode方法返回两个看上去是随机的数值,而不是约定中要求的相等的值。 
    原帖地址:http://www.java3z.com/cwbwebhome/article/article2/2342.html?id=961还是不能系统的理解?哪位高人可否告知如果想系统完整深入的了解hashcode需要掌握哪些知识?
      

  13.   

    曾经看到一个人的解释
    就好比 带有编号的猪圈里 有多少猪
    这个编号就是hashcode
    你要比较猪是否相同 通过hashcode(及猪圈编号)直接定位到一个猪圈
    再到这个猪圈来找猪 肯定效率会提高

    但是不知道原文在哪里了
      

  14.   

    Object p1=new Object(); 
    Object p2=new Object(); 
    p1和p2都是Object的引用也就是说p1.valuse=p2.valuse,但是如果不重写equals()的时候p1!=p2->p1.valuse!=p2.valuse。所以我们把它重写,让他不是比较对象是否相等而是比较值是不是相等。
    -----------汗,还是不解, p1!=p2  ->  p1.valuse!=p2.valuse。 他们之间有必然的联系么?p1.valuse  这个是什么?   p1.valuse=p2.valuse   是赋值吗??
      

  15.   

    通过put方法,放在hashmap等集合中时,
    HashMap的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;也就是说先比较hashcode值是否相等,相等的情况下,再进行判断equals方法是否返回true值,
    因此hashCode()和equals(Object o)两个方法都要重写。
    而get(Object o)方法的源代码:    public V get(Object key) {
            if (key == null)
                return getForNullKey();
            int hash = hash(key.hashCode());
            for (Entry<K,V> e = table[indexFor(hash, table.length)];
                 e != null;
                 e = e.next) {
                Object k;
                if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
                    return e.value;
            }
            return null;
        }可以看见hashCode()和equals(Object o)的调用。
      

  16.   

    3楼的朋友好,可以参看我BLOG中的《Java编程那些事儿》中关于Object类的介绍。
    -------------
    找不到啊, 能告诉具体哪一章么?
      

  17.   

    找到一篇关联文章,放在这里,仔细研读研读
    --------2008-11-23 22:51Hashtable从JDK1.0就已经有了, 所以让我们先来看看它是怎么工作, 然后有浅入深, 来研究HashMap的原理, 以及两者的不同点.Hashtable有几个主要的字段, 如下,/**
    * The hash table data.
    */
    private transient Entry[] table;/**
    * The total number of entries in the hash table.
    */
    private transient int count;/**
    * The table is rehashed when its size exceeds this threshold. (The
    * value of this field is (int)(capacity * loadFactor).)
    *
    * @serial
    */
    private int threshold;其中最重要的就是那个table数组了. 它就是整个hashtable的基本数据结构! 在来看一下这个字段
    private transient Entry[] table;可以看到, hashtable的基本数据结构就是, 一个包涵Entry类的二维数组. 而这个Entry类是hashtable的内在类, 它其实是一个单向链, 让我们详细分析一下.
    private static class Entry<K,V> implements Map.Entry<K,V> {
    int hash;
    K key;
    V value;
    Entry<K,V> next;
    ...
    ...看到这里有没有想到学校里教的数据结构原理这门课呢? Entry类就是定义了一个很简单的单向链结构, 它里面包括key, value和下个Entry类的对象next.
    在这里我在强调一下, hashtable的数据结构就是一个包涵单向链的二维数组.
    接下来让我们来看看hashtable的构造器是长的什么样的.最长用的public Hashtable() {
    this(11, 0.75f);
    }这个构造器调用了另外一个构造器public Hashtable(int initialCapacity, float loadFactor) {
    if (initialCapacity < 0)
    throw new IllegalArgumentException("Illegal Capacity: "+
    initialCapacity);
    if (loadFactor <= 0 || Float.isNaN(loadFactor))
    throw new IllegalArgumentException("Illegal Load: "+loadFactor);if (initialCapacity==0)
    initialCapacity = 1;
    this.loadFactor = loadFactor;
    table = new Entry[initialCapacity];
    threshold = (int)(initialCapacity * loadFactor);
    }细读代码后, 我们发现这个构造器构造了table字段和threshold. Table前面已经详细讲了, 那么这个threshold又是什么东东呢?
    其实这个threshold对hashtalbe的性能影响是很大的! 因为table是个数组, 如果在hashtable中保存的实体大于一定的数量后, 对数据的读写就会有很慢, 那是因为, 很多数据都保存在entry类的单向链中, 每次读写都要比对链中所有的数据, 链越长读写就越慢.
    所以当数据容量大于threshold的时候, hashtable就会做rehash(), rehash把table的容量扩大一倍, 再把从前在table里的数据统统搬回新的table. 这样的一个过程, 开销是多么的大呀.
    threshold = (int)(initialCapacity * loadFactor);
    Hashtable类提供了构造涵数, 用户可以自定, intitialCapacity和loadFactor. 对于那些大概知道容量的hashtable, 用户应该自定intitialCapacity. 这样的话, 就可以省去一大笔rehash的开销.现在让我们来看hashtable的put和get操作
    public synchronized V get(Object key) {
    Entry tab[] = table;
    int hash = key.hashCode();
    int index = (hash & 0x7FFFFFFF) % tab.length;
    for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
    if ((e.hash == hash) && e.key.equals(key)) {
    return e.value;
    }
    }
    return null;
    }先来看get方法, get可谓是hashtable中的最基本方法了, 它是通过key来拿到hashtable中的value.
    int hash = key.hashCode();
    int index = (hash & 0x7FFFFFFF) % tab.length;
    从key拿到hashCode, 从hashCode再计算出在table中的index, 也就是在数组中的第几个列.
    至于为什么要与 0x7FFFFFFF, 那是hashtable 提供的hash算法, hashMap提供了不同的算法, 用户如果要定义自己的算法也是可以的. 如果要知道不同的具体算法, 就google or 百度一下吧.好了, 现在我们有了index, 就可以到table数组里的entry单向链去找value啦.for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
    if ((e.hash == hash) && e.key.equals(key)) {
    return e.value;
    }
    }for语句就是简单的检索entry的单链, if语句检查key是否相同. 这里就遇到了java学习中的一个重大知识点. hasCode()和equal()的关系.
    大家都学过如果hasCode()的值相同的话, equal不一定相同, 而如果equal相同的话, hasCode一定要相同. 但那是为什么呢? 其实答案就在上面的代码中!
    Hashtable 的数据结构是一个包涵单向链的二维数组. 从hasCode我们得到hash和index, 并得以确定这个key在table数组中的第几个列, 然而这显然是不够的, 因为, entry类是一个单向列, 它可以是一个, 也可能是很多个key组成, 那么要从一系列有着相同hasCode的entry中找到, 我们所要的key的话, 就要用equals了. 只有两个key是相等的, 那才是我们要找的. 找到key之后, 只要简单的把value返回就好了. 如果对entry类还有疑问的话, 请参考前面的解释.public synchronized V put(K key, V value) {
    // Make sure the value is not null
    if (value == null) {
    throw new NullPointerException();
    }// Makes sure the key is not already in the hashtable.
    Entry tab[] = table;
    int hash = key.hashCode();
    int index = (hash & 0x7FFFFFFF) % tab.length;
    for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
    if ((e.hash == hash) && e.key.equals(key)) {
    V old = e.value;
    e.value = value;
    return old;
    }
    }modCount++;
    if (count >= threshold) {
    // Rehash the table if the threshold is exceeded
    rehash();tab = table;
    index = (hash & 0x7FFFFFFF) % tab.length;
    } // Creates the new entry.
    Entry<K,V> e = tab[index];
    tab[index] = new Entry<K,V>(hash, key, value, e);
    count++;
    return null;
    }接下来再来看看put方法, 理解了get, put就很容易弄明白了.
    首先, 要放入hashtable的value不能是null, 否则就报错.
    其次, 然后要确保key不能已经在hashtable里面, 有的话, 就返回value.
    再次, 检查是否容量已经太大, 如果太大话就rehash, 这会是一个很浪费资源的方法, 请参考前文.最后, 也是最重要的, 我们要把key-value保存到hashtable中去.Entry<K,V> e = tab[index];
    tab[index] = new Entry<K,V>(hash, key, value, e);1.拿到当前在table数组中的entry对象.
    2.根据传入的key和value建一个新的entry并赋予给当前的table的index中
    protected Entry(int hash, K key, V value, Entry<K,V> next) {
    this.hash = hash;
    this.key = key;
    this.value = value;
    this.next = next;
    }
    这是entry类的构造函数. 简单的说, 就是在单链的最前端加了个新的entry对象. 从这里也可以看出, 对于那些后写入的object, 反而可以以比较快的速度读出, 那是因为后写入的object, 总是在链的前端.看完了hashtable, 我们在来看看hashMap
    hashMap可以算作是hashtable的升级版本, 最早从1.2开始有的.
    整体上hashMap对hashtable类优化了代码. 比如说, 消除了hardcoding, 增加了code reuse等等.
    但是, 两者之间最主要的不同有两点.
    1.hashMap的读写是unsynchronized, 在多线程的环境中要注意使用
    而hashtable是synchronized
    这两者的不同是通过在读写方法上加synchronized关键字来实现的.hashMap
    public V put(K key, V value)
    public V get(Object key)hashtable
    public synchronized V get(Object key)
    public synchronized V put(K key, V value)可能有人问, 能synchronized, 能线程安全好啊. 为什么不要呢?
    这里其实还是一个效率的问题. 对于线程安全的方法, 系统要进行加锁, 减锁操作. 性能会有很大的影响. 由于很多程序是在单线程或者说是线程安全的情况下工作的, 所以用synchronized就显得多余了.3.第二个不同是hashMap可以放空值, 而hashtable就会报错.
    hashMap
    public V put(K key, V value) {
    if (key == null)
    return putForNullKey(value);hashtable
    public synchronized V put(K key, V value) {
    // Make sure the value is not null
    if (value == null) {
     
      

  18.   

    这个视频有讲解哈希表,似乎明白了些
    http://video.baidu.com/v?ct=301989888&rn=20&pn=0&db=0&s=25&word=%B9%FE%CF%A3%B1%ED%CA%FD%BE%DD%BD%E1%B9%B9
      

  19.   

    两者都是Object的方法,
    public boolean equals(Object obj) :指示其他某个对象是否与此对象“相等”。 
        equals 方法在非空对象引用上实现相等关系:       自反性:对于任何非空引用值 x,x.equals(x) 都应返回 true。 
          对称性:对于任何非空引用值 x 和 y,当且仅当 y.equals(x) 返回 true 时,x.equals(y) 才应返回 true。 
          传递性:对于任何非空引用值 x、y 和 z,如果 x.equals(y) 返回 true,并且 y.equals(z) 返回 true,那么     x.equals(z) 应返回 true。 
          一致性:对于任何非空引用值 x 和 y,多次调用 x.equals(y) 始终返回 true 或始终返回 false,前提是对象上 equals 比较中所用的信息没有被修改。 
         对于任何非空引用值 x,x.equals(null) 都应返回 false。 
    Object 类的 equals 方法实现对象上差别可能性最大的相等关系;即,对于任何非空引用值 x 和 y,当且仅当 x 和 y 引用同一个对象时,此方法才返回 true(x == y 具有值 true)。      注意:当此方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。 
    public int hashCode():返回该对象的哈希码值。支持此方法是为了提高哈希表(例如 java.util.Hashtable 提供的哈希表)的性能。 
    hashCode 的常规协定是:       在 Java 应用程序执行期间,在对同一对象多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是将对象进行 equals 比较时所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。 
    如果根据 equals(Object) 方法,两个对象是相等的,那么对这两个对象中的每个对象调用 hashCode 方法都必须生成相同的整数结果。 
         如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么对这两个对象中的任一对象上调用 hashCode 方法不 要求一定生成不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能。 
    实际上,由 Object 类定义的 hashCode 方法确实会针对不同的对象返回不同的整数。(这一般是通过将该对象的内部地址转换成一个整数来实现的,但是 JavaTM 编程语言不需要这种实现技巧。)