在每个覆盖了equals方法的类中,必须覆盖hashCode方法,否则导致该类无法结合基于散列的集合一起正常工作,这些集合包括HashMap,HashSet,HashTable(废弃)
Object规范:1,在应用程序执行期间,只要对象的equals方法比较的操作用到的信息没有被修改,那么对同一个对象调用多次HashCode方法都必须始终返回同一个整数。在同一个应用程序的多次执行过程中,每次执行所返回的的整数可以不一致。
2,如果两个对象根据equals方法比较是相等的,那么调用两个对象的中任意一个对象的hashCode方法都必须产生相同的整数结果。
3,如果两个对象根据equals(Object)方法比较是不相等的,那么调用这两个对象中任意一个hashCode方法,则不一定要产生相同的整数结果。但是应该知道,给不相同的对象产生截然不同的整数结果,有可能提高散列表的性能。
根据类的equals方法,两个截然不同的实例在逻辑上有可能是相等的但是根据Object类的HashCode方法,他们仅仅是两个没有任何相同之处的对象。因此对象的hashCode方法返回两个看起来是随机的整数,而不是根据第二个约定所要求的那样,返回两个相等的整数。
为不相等的对象产生不相等的散列码
/** * @Description * @Author liam661 * @Date 2021/4/19 21:30 **/ 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); } } @Override public boolean equals(Object o){ if(o == this) return true; if(!(o instanceof PhoneNumber)){ return false; } PhoneNumber pn = (PhoneNumber) o; return pn.lineNumber == lineNumber && pn.prefix == prefix && pn.areaCode == areaCode; } public static void main(String [] args){ PhoneNumber p1 = new PhoneNumber(77,867,5309); PhoneNumber p2 = new PhoneNumber(77,867,5309); Map<PhoneNumber,String> m = new HashMap<PhoneNumber,String>(); m.put(p1,"jeeny"); m.put(p2,"lining"); Set<PhoneNumber> set = new HashSet<PhoneNumber>(); set.add(p1); set.add(p2); System.out.println(m.get(p1)); System.out.println(m.get(p2)); System.out.println(set); } }
null [liam.mon4.PhoneNumber@2437c6dc, liam.mon4.PhoneNumber@6ed3ef1]
由于PhoneNumber类覆盖hashCode方法,从而导致两个相同的实例具有不相同的散列码,违反了hashCode的约定
@Override public int hashCode(){ int result = 17; result = 31 * result + areaCode; result = 31 * result + prefix; result = 31 * result + lineNumber; return result; }
lining
[liam.mon4.PhoneNumber@95916]
如果一个类是不变的,并且计算散列码的开销也很大,就应该考虑把散列码缓存在对象内部,而不是每次请求的时候都重新计算散列码。
@Override private volatile int hashCode; public int hashCode(){ int result = hashCode; if(result == 0){ result = 31 * result + areaCode; result = 31 * result + prefix; result = 31 * result + lineNumber; hashCode = result; } return result; }
但是不能产生最新的散列函数。
不要试图从散列码计算中排除掉一个对象的的关键部分来提高性能。
摘抄自《effective java 2》