重寫equals()方法時,需要同時重寫hashCode()方法

qingyezhu發表於2015-02-01
package com.wangzhu.map;

import java.util.HashMap;

/**
 * hashCode方法的主要作用是為了配合基於雜湊的集合一起正常執行,<br/>
 * 這樣的雜湊集合包括HashSet、HashMap以及HashTable。<br/>
 * 能否可以直接根據hashCode值判斷兩個物件是否相等呢?<br/>
 * 答案:肯定是不可以的,因為不同的物件可能會生成相同的hashCode值。<br/>
 * 雖然不能根據hashCode值判斷兩個物件是否相等,但是可以直接根據hashCode值判斷兩個物件不等,<br/>
 * 如果兩個物件的hashCode值不等,則必定是兩個不同的物件。如果要判斷兩個物件是否真正相等,則必須通過equals方法。<br/>
 * 也就是說對於兩個物件,如果呼叫equals方法得到的結果為true,則兩個物件的hashCode值必定相等。<br/>
 * 如果equals方法得到的結果為,則兩個物件的hashCode值不一定不同; <br/>
 * 如果兩個物件的hashCode值不等,則equals方法得到的結果必為;<br/>
 * 如果兩個物件的hashCode值相等,則equals方法得到的結果未知。<br/>
 * 
 * @author wangzhu
 * @date 2015-2-1下午11:22:42
 * 
 */
public class DemoMap2 {

    /**
     * @param args
     */
    public static void main(String[] args) {
        Student stu = new Student(21, "John");
        HashMap<Student, Integer> map = new HashMap<Student, Integer>();
        map.put(stu, 1);
        System.out.println(map.get(stu));// 1
        System.out.println(map.get(new Student(21, "John")));// 1
        /**
         * 若Student只重寫equals方法而沒有沒有重寫hashCode方法時,則輸出為null
         */

        stu.setAge(22);
        System.out.println(map.get(stu));// null
        /**
         * 在程式執行期間,只要equals方法的比較操作用到的資訊沒有被修改,<br/>
         * 那麼對這同一個物件呼叫多次, hashCode方法必須始終如一地返回同一個整數。<br/>
         * 
         * 設計hashCode()時最終稿的因素就是:無論何時,對同一個物件呼叫hashCode()都應該產生同樣的值。<br/>
         * 如果在將一個物件用put()新增進HashMap時產生一個hashCode值,而用get()取出時卻產生另一個hashCode值,<br/>
         * 那麼就無法獲取該物件了。所以如果你的hashCode方法依賴於物件中易變的資料,使用者就要當心了,<br/>
         * 因為此資料變化時,hashCode() 方法就會生成一個不同的雜湊碼。<br/>
         */
    }

}

class Student {
    int age;
    String name;

    public Student(int age, String name) {
        super();
        this.age = age;
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = (prime * result) + age;
        result = (prime * result) + ((name == null) ? 0 : name.hashCode());
        return result;
    }

    /**
     * 重寫時,需要讓equals方法與hashCode方法始終在邏輯上保持一致性。
     */
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (this.getClass() != obj.getClass()) {
            return false;
        }
        Student other = (Student) obj;
        if (age != other.age) {
            return false;
        }
        if (name == null) {
            if (other.name != null) {
                return false;
            }
        } else if (!name.equals(other.name)) {
            return false;
        }
        return true;
    }

}

 

下面是HashMap中的put方法【JDK1.6】

    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;
    }

備註:put方法是用來向HashMap中新增新的元素。從put方法的具體實現可知,其會先呼叫hashCode方法得到該元素的hashCode值【經過處理的】,然後檢視其是否存在於table陣列中,如果存在則呼叫equals方法來確定是否是存在該元素;如果存在,則更新value值,否則將新的元素新增到HashMap中。

 

下面是HashMap中的get方法【JDK1.6】

 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;
    }

備註:get方法是用來將HashMap中對應Key的值Value取出來。從get方法的具體實現可知,其會先呼叫hashCode方法得到該元素的hashCode值,然後檢視其在table陣列中是否存在,如果存在則呼叫equals方法來確定是Key對應的元素,並返回其Value,否則返回null。

 

相關文章