import java.util.HashMap;
import java.util.Map;
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, "lineNumber");
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);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
PhoneNumber other = (PhoneNumber) obj;
if (areaCode != other.areaCode)
return false;
if (lineNumber != other.lineNumber)
return false;
if (prefix != other.prefix)
return false;
return true;
}
public static void main(String[] args) {
Map<PhoneNumber,String> map = new HashMap<PhoneNumber,String>();
map.put(new PhoneNumber(707, 867, 5309), "jenny");
System.out.println(map.get(new PhoneNumber(707, 867, 5309)));
}
}
例如上面这个类,覆盖了equals方法但是没有覆盖hashCode方法,在我们map.get(new PhoneNumber(707, 867, 5309))时候,会认为输出的结果会是“Jenny”,但是它实际上输出的结果是null;这是因为,PhoneNumber类没有覆盖hashCode方法,从而导致两个相等的实例对象具有不同的散列码,违反了hashCode的约定。
但是如果main函数中是这样的:
public static void main(String[] args) {
Map<PhoneNumber,String> map = new HashMap<PhoneNumber,String>();
PhoneNumber phoneNumber = new PhoneNumber(707, 867, 5309);
map.put(phoneNumber, "jenny");
System.out.println(map.get(phoneNumber));
}
创建了一个PhoneNumber对象的引用,在将它放入map中,此时,无论PhoneNumber类有没有覆盖hashCode方法,输出的结果也都是“Jenny”;虽然PhoneNumber类有没有覆盖hashCode方法,但是map的put与get都是同一个实例对象的引用。
回到正题,既然没有覆盖hashCode函数会导致出现这样的一个问题,那么,我们就需要提供一个hashCode方法,例如
@Override
public int hashCode() {
return 14;
}
虽然这个hashCode方法是合法的,但是它使得每个对象都具有相同的散列值,每个对象都被映射到同一个散列通(hash bucket)中,使散列表退化为(linked list)。它使得本该线性时间允许的程序变成了以平方级时间在运行。
因此,要提供一个合适的hashCode方法才是主要的。例如
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + areaCode;
result = prime * result + lineNumber;
result = prime * result + prefix;
return result;
}
31有个很好的特性,即用移位和减法来代替乘法,可以得到更好的性能:31*i==(i<<5)-i。现代的VM可以自动完成这种优化。