今天做面試題的時候遇到了一個問題,問題的代碼如下:
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, "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); } }@Overridepublic 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);return false; } public static void main(String[] args){ Map<PhoneNumber, String> m=new HashMap<PhoneNumber, String>(); PhoneNumber pn=new PhoneNumber(707,067, 5309); m.put(new PhoneNumber(707,067, 5309), "jenny"); System.out.println(m.get(new PhoneNumber(707,067, 5309))); }}
而問題就是看看程式執行完後的輸出結果,我當時一看此題有個疑問但是不確定,他沒有重寫hashCode方法,又想起equals方法與hashCode的方法建議是一起出現的,所以想到此題有陷阱,但是又不知道什麼地方有陷阱。把程式敲了一遍發現輸出結果為null,猜到肯定與hashCode相關,於是在Map的put方法前加了一個端點,進入了hashmap的源碼進行單步調試,我發現源碼中在存取key的時候調用了hashcode方法,而預設如果不重寫hashcode方法是調用的預設的hashcode,即對象的雜湊碼,所以不同的對象有不同的雜湊碼,然後他根據雜湊碼進行儲存。當調用get方法時又根據雜湊碼進行取值,如果值存在才進行eqals方法的調用,所以即使equals方法返回是true也是沒有用的需要重寫hashCode讓其返回相同的雜湊碼。我將代碼改成如下形式後返回了jenny。
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, "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); } }@Overridepublic 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);return false; }@Overridepublic int hashCode(){return 7*(new Integer(lineNumber).hashCode())+11*(new Integer(prefix)).hashCode()+13*(new Integer(areaCode).hashCode());} public static void main(String[] args){ Map<PhoneNumber, String> m=new HashMap<PhoneNumber, String>(); PhoneNumber pn=new PhoneNumber(707,067, 5309); m.put(new PhoneNumber(707,067, 5309), "jenny"); System.out.println(m.get(new PhoneNumber(707,067, 5309))); System.out.println(pn.getClass()); }}
現在說一下equals方法和==的區別,==預設是比較兩個對象的引用是否相等,equsls如果沒被重寫預設也是按照==進行比較。==一般用來比較兩個基本類型是否相等,我們在比較字串相等的時候也用equals方法,那是因為在String類中已經將equals方法和hashCode方法進行了覆寫,比較的是值的大小。