- Object中的定义
/**
* Returns a hash code value for the object. This method is
* supported for the benefit of hash tables such as those provided by
* {@link java.util.HashMap}.
* <p>
* The general contract of {@code hashCode} is:
*
* 每当在Java应用程序的执行过程中多次调用同一个对象时,
* @code hashCode方法必须始终返回相同的整数,
* 前提是在@code中使用的信息没有被修改。
* 这个整数不需要保持一致,从一个应用程序的执行到同一个应用程序的另一个执行。
* <li>Whenever it is invoked on the same object more than once during
* an execution of a Java application, the {@code hashCode} method
* must consistently return the same integer, provided no information
* used in {@code equals} 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.
*
* 如果根据@code equals(Object)方法,两个对象是相等的,
* 那么在这两个对象上调用@code hashCode方法必须产生相同的整数结果。
* <li>If two objects are equal according to the {@code equals(Object)}
* method, then calling the {@code hashCode} method on each of
* the two objects must produce the same integer result.
*
* 如果两个对象是不相等的,这是不需要的
* 根据@link java.lang.object equals(java.lang.object)方法,
* 然后在每一个上调用@code hashCode方法,两个对象必须产生不同的整数结果。
* 然而,程序员应该意识到产生不同的整数结果对于不相等的对象,可以提高散列表的性能。
* <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 {@code hashCode} 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 hash tables.
* </ul>
* <p>
* As much as is reasonably practical, the hashCode method defined by
* class {@code Object} 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™ programming language.)
*
* @return a hash code value for this object.
* @see java.lang.Object#equals(java.lang.Object)
* @see java.lang.System#identityHashCode
*/
public native int hashCode();
/**
* Indicates whether some other object is "equal to" this one.
* <p>
* The {@code equals} 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 x.equals(x)} should return
* {@code true}.
* <li>It is <i>symmetric</i>: for any non-null reference values
* {@code x} and {@code y}, {@code x.equals(y)}
* should return {@code true} if and only if
* {@code y.equals(x)} returns {@code true}.
* <li>It is <i>transitive</i>: for any non-null reference values
* {@code x}, {@code y}, and {@code z}, if
* {@code x.equals(y)} returns {@code true} and
* {@code y.equals(z)} returns {@code true}, then
* {@code x.equals(z)} should return {@code true}.
* <li>It is <i>consistent</i>: for any non-null reference values
* {@code x} and {@code y}, multiple invocations of
* {@code x.equals(y)} consistently return {@code true}
* or consistently return {@code false}, provided no
* information used in {@code equals} comparisons on the
* objects is modified.
* <li>For any non-null reference value {@code x},
* {@code x.equals(null)} should return {@code false}.
* </ul>
* <p>
* The {@code equals} method for class {@code Object} implements
* the most discriminating possible equivalence relation on objects;
* that is, for any non-null reference values {@code x} and
* {@code y}, this method returns {@code true} if and only
* if {@code x} and {@code y} refer to the same object
* ({@code x == y} has the value {@code true}).
*
* 请注意,每当这个方法被覆盖时,通常都需要覆盖@code hashCode方法,
* 以便维护@code hashCode方法的一般契约,
* 该方法规定相等对象必须具有相等的散列码。
* Note that it is generally necessary to override the {@code hashCode}
* method whenever this method is overridden, so as to maintain the
* general contract for the {@code hashCode} method, which states
* that equal objects must have equal hash codes.
*
* @param obj the reference object with which to compare.
* @return {@code true} if this object is the same as the obj
* argument; {@code false} otherwise.
* @see #hashCode()
* @see java.util.HashMap
*/
public boolean equals(Object obj) {
return (this == obj);
}
- 重写equals一定要重写hashCode
由Object。hashCode的通用约定,我们可以直观地看出:覆盖equals必须覆盖hashCode。
如果没有覆盖hashCode会违反Oject.hashCode通用约定的第二条:相等的对象必须具有相等的散列码,导致该类无法结合所有基于散列的集合(HashMap、HashSet和Hashtable)一起正常运作。同时,不相等的对象产生截然不同的hash结果,有利于提高散列表的性能。
equals与hashCode都用鉴别是否是同一对象,前者比较行为比较为全面复杂(意味着效率低),后者只要用哈希值比较就行(效率高)。但是利用hashCode比较不是绝对可靠的。所以为提高效率: 一般先利用hashCode去比较,如果不一样,那么它们就不相等;否则,再用equals比较。这样会大大提高比较的效率。
注:不同对象的hashCode一定不同吗?不一定(这可能是因为:生成哈希值的算法可能存在问题或哈希值的长度限制)
举个例子,
public final class PhoneNumber {
private final short areaCode;
private final short prefix;
private final short lineNumber;
public PhoneNumber(int areaCode, int prefix, int lineNumber) {
rangeCheck(areaCode,999,"area code");
rangeCheck(prefix,999,"prefix");
rangeCheck(lineNumber,9999,"line number");
this.areaCode = (short)areaCode;
this.prefix = (short)prefix;
this.lineNumber = (short)lineNumber;
}
private static void rangeCheck(int arg,int max,String name){
if(arg<0||arg>max)
{
throw new IllegalArgumentException(name+":"+arg);
}
}
/**
* 这个hashCode是合法的,它确保了相等的对象具有同样的散列码
* 同时,这种定义极为恶劣,因为它使每个对象都具有相同的散列码。
* 这样会使每个对象都被映射到同一个散列桶中,使散列表退化为链表(本该线性时间运行变为了平方时间运行)
* 注:此处这样写仅为方便地演示具体事例。
*/
// @Override
// public int hashCode()
// {
// return 42;
// }
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if(!(obj instanceof PhoneNumber))
return false;
PhoneNumber pn=(PhoneNumber)obj;
return pn.lineNumber==lineNumber
&&pn.prefix==prefix
&&pn.areaCode==areaCode;
}
}
import java.util.HashMap;
import java.util.Map;
public class Test {
public static void main(String[] args) {
Map<PhoneNumber, String> m=new HashMap<PhoneNumber, String>();
m.put(new PhoneNumber(707, 867,5309),"Jenny");
System.out.println(m.get(new PhoneNumber(707, 867,5309)));
}
}
当我们期待通过m.get(new PhoneNumber(707, 867,5309))返回Jenny的时候,实际输出结果为null。为什么?因为没有重写hashCode方法,导致相等的实例的散列码不同。put方法将电话号码对象存放在一个散列桶中,get方法却在另一个散列桶中查找这个电话号码。(即使这两个正好放在一个桶中,get方法也会返回null。因为HashMap有一项优化,可以将每个项相关联的散列码缓存起来,如果散列码不匹配,也不必检验对象的等同性)
如何修正这个问题?只需在PhoneNumber 类中重写一个合适的HashCode。
我们将PhoneNumber 中的HashCode取消注释,再运行效果就出来了。
参考文献《Effective Java》