問:HashMap 中的 key 如果是 Object 則需要實現(xiàn)哪些方法?
答:hashCode 方法和 equals 方法。
因為 hashCode 方法用來計算 Entry(記錄) 在數(shù)組中的 index 索引位置,equals 方法用來比較數(shù)組指定 index 索引位置上鏈表的節(jié)點 Entry 元素是否相等。否則由于 hashCode 方法實現(xiàn)不恰當會導(dǎo)致嚴重的 hash 碰撞,從而使 HashMap 會退化成鏈表結(jié)構(gòu)而影響性能。
問:下面兩種遍歷方式有什么區(qū)別?為什么?
//第一種
Map map = new HashMap();
Iterator iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
Object key = entry.getKey();
Object val = entry.getValue();
}
//第二種
Map map = new HashMap();
Iterator iter = map.keySet().iterator();
while (iter.hasNext()) {
Object key = iter.next();
Object val = map.get(key);
}
答:第一種方式效率高且推薦用。
因為 HashMap 的這兩種遍歷是分別對 keySet 和 entrySet 進行迭代,對于 keySet 實質(zhì)上是遍歷了兩次,一次是轉(zhuǎn)為 iterator 迭代器遍歷,一次就從 HashMap 中取出 key 所對于的 value 操作(通過 key 值 hashCode 和 equals 索引);而 entrySet 方式只遍歷了一次,它把 key 和 value 都放到了 Entry 中,所以效率高。
問:為什么 HashMap 中 String、Integer 這樣的包裝類適合作為 key 鍵,即為什么使用它們可以減少哈希碰撞?
答:因為 String、Integer 等包裝類是 final 類型的,具有不可變性,而且已經(jīng)重寫了 equals() 和 hashCode() 方法。不可變性保證了計算 hashCode() 后鍵值的唯一性和緩存特性,不會出現(xiàn)放入和獲取時哈希碼不同的情況且讀取哈希值的高效性,此外官方實現(xiàn)的 equals() 和 hashCode() 都是嚴格遵守相關(guān)規(guī)范的,不會出現(xiàn)錯誤。
問:下面程序的輸出結(jié)果是什么?
class Item {
public String name;
public int age;
@Override
public int hashCode() {
return this.name.hashCode() + this.age;
}
public Item(String name, int age) {
this.name = name;
this.age = age;
}
}
public class Demo {
public static void main(String[] args) {
Item item1 = new Item("android", 4);
Item item2 = new Item("java", 3);
Map<Item, Item> map = new HashMap<Item, Item>();
map.put(item1, item1);
map.put(item2, item2);
item2.name = "unix c"; //相當于修改了key值
Item value = map.get(item2);
System.out.println("value=" + value);
}
}
答:輸出結(jié)果為 value=null。
因為 key 更新后 hashCode 也更新了,(這里是因為重寫了 hashcode 的原因)而 HashMap 里面的對象是我們原來哈希值的對象,在 get 時由于哈希值已經(jīng)變了,原來的對象不會被索引到了,所以結(jié)果為 null,因此當把對象放到 HashMap 后就不要嘗試對 key 進行修改操作,謹防出現(xiàn)哈希值變化或者 equals 比較不等的情況導(dǎo)致無法索引。