關於Java覆蓋equals方法時必須覆蓋hashCode方法
阿新 • • 發佈:2018-12-07
Java中的物件自動繼承Object類,而Object類實現了equals方法和hashCode方法:
/**
* Returns a hash code value for the object. This method is
* supported for the benefit of hashtables such as those provided by
* <code>java.util.Hashtable</code>.
* <p>
* The general contract of <code>hashCode</code> is:
* <ul>
* <li>Whenever it is invoked on the same object more than once during
* an execution of a Java application, the <tt>hashCode</tt> method
* must consistently return the same integer, provided no information
* used in <tt>equals</tt> comparisons on the object is modified.
* This integer need not remain consistent from one execution of an
* application to another execution of the same application.
* <li>If two objects are equal according to the <tt>equals(Object)</tt>
* method, then calling the <code>hashCode</code> method on each of
* the two objects must produce the same integer result.
* <li>It is <em>not</em> required that if two objects are unequal
* according to the {@link java.lang.Object#equals(java.lang.Object)}
* method, then calling the <tt>hashCode</tt> method on each of the
* two objects must produce distinct integer results. However, the
* programmer should be aware that producing distinct integer results
* for unequal objects may improve the performance of hashtables.
* </ul>
* <p>
* As much as is reasonably practical, the hashCode method defined by
* class <tt>Object</tt> does return distinct integers for distinct
* objects. (This is typically implemented by converting the internal
* address of the object into an integer, but this implementation
* technique is not required by the
* Java<font size="-2"><sup>TM</sup></font> programming language.)
*
* @return a hash code value for this object.
* @see java.lang.Object#equals(java.lang.Object)
* @see java.util.Hashtable
*/
public native int hashCode();
/**
* Indicates whether some other object is "equal to" this one.
* <p>
* The <code>equals</code> method implements an equivalence relation
* on non-null object references:
* <ul>
* <li>It is <i>reflexive</i>: for any non-null reference value
* <code>x</code>, <code>x.equals(x)</code> should return
* <code>true</code>.
* <li>It is <i>symmetric</i>: for any non-null reference values
* <code>x</code> and <code>y</code>, <code>x.equals(y)</code>
* should return <code>true</code> if and only if
* <code>y.equals(x)</code> returns <code>true</code>.
* <li>It is <i>transitive</i>: for any non-null reference values
* <code>x</code>, <code>y</code>, and <code>z</code>, if
* <code>x.equals(y)</code> returns <code>true</code> and
* <code>y.equals(z)</code> returns <code>true</code>, then
* <code>x.equals(z)</code> should return <code>true</code>.
* <li>It is <i>consistent</i>: for any non-null reference values
* <code>x</code> and <code>y</code>, multiple invocations of
* <tt>x.equals(y)</tt> consistently return <code>true</code>
* or consistently return <code>false</code>, provided no
* information used in <code>equals</code> comparisons on the
* objects is modified.
* <li>For any non-null reference value <code>x</code>,
* <code>x.equals(null)</code> should return <code>false</code>.
* </ul>
* <p>
* The <tt>equals</tt> method for class <code>Object</code> implements
* the most discriminating possible equivalence relation on objects;
* that is, for any non-null reference values <code>x</code> and
* <code>y</code>, this method returns <code>true</code> if and only
* if <code>x</code> and <code>y</code> refer to the same object
* (<code>x == y</code> has the value <code>true</code>).
* <p>
* Note that it is generally necessary to override the <tt>hashCode</tt>
* method whenever this method is overridden, so as to maintain the
* general contract for the <tt>hashCode</tt> method, which states
* that equal objects must have equal hash codes.
*
* @param obj the reference object with which to compare.
* @return <code>true</code> if this object is the same as the obj
* argument; <code>false</code> otherwise.
* @see #hashCode()
* @see java.util.Hashtable
*/
public boolean equals(Object obj) {
return (this == obj);
}
Object類中的hashCode()已自動實現為不同的物件生成不同的hashCode,所以,當我們建立一個新的類時,即使以這個類為基準建立了兩個完全相同的物件(物件中的元素完全相同),這兩個相同物件的hashCode也是不同的:
import java.util.Map; import java.util.Map.Entry; import com.google.common.base.Objects; import com.google.common.collect.Maps; public class Name { String id; Name(String id) { this.id = id; } @Override public boolean equals(Object obj) { Name otherName = (Name) obj; return Objects.equal(this.id, otherName.id); } @Override public String toString() { return this.getClass().getSimpleName() + "@" + this.id + "&" + this.hashCode(); } public static void main(String[] args) { Name n1 = new Name("zhao"); Name n2 = new Name("zhao"); System.err.println("n1.equals(n2) = " + n1.equals(n2)); Map<Name, Integer> map = Maps.newHashMap(); map.put(n1, 1); map.put(n2, 2); for (Entry<Name, Integer> entry : map.entrySet()) { System.out.println(entry.getKey() + ":" + entry.getValue()); } System.err.println(map.get(new Name("zhao"))); } }
而Java中的Hash容器會根據Key的hashCode,將不同hashCode物件(即hashCode()不同的物件而非equals()不同的物件)雜湊到不同的位置,這時候在Hash容器中應用這些物件時會出現錯誤。所以,在覆蓋equals()方法時必須覆蓋hashCode()方法。如下是HashMap原始碼實現:
/** * Associates the specified value with the specified key in this map. * If the map previously contained a mapping for the key, the old * value is replaced. * * @param key key with which the specified value is to be associated * @param value value to be associated with the specified key * @return the previous value associated with <tt>key</tt>, or * <tt>null</tt> if there was no mapping for <tt>key</tt>. * (A <tt>null</tt> return can also indicate that the map * previously associated <tt>null</tt> with <tt>key</tt>.) */ 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; }
參考文章:
1、JDK原始碼
2、http://www.cnblogs.com/wangliyue/p/4450747.html
3、http://www.cnblogs.com/happyPawpaw/p/3744971.html