一、概述
在Map中存儲的每一個鍵值對都是以一個Map.Entry<K,V>的實現對象存儲的,Map.Entry是一個接口,不能實例化,所以Map的不同實現類,存儲鍵值對的方式也會有所不同,只要這個鍵值對的類實現了Map.Entry接口即可。
在HashMap中鍵值對的存儲方式有兩種:
其中一種是我們比較熟知的鏈表存儲結構,也就是以HashMap.Node<K,V>類的對象方式存儲的, Node類是HashMap的一個靜態內部類,實現了 Map.Entry<K,V>接口。在調用put方法創建一個新的鍵值對時,會調用newNode方法來創建Node對象。
二、方法解析
// 該方法很簡單,只是調用了Node類的構造函數
Node<K,V> newNode(int hash, K key, V value, Node<K,V> next) {
return new Node<>(hash, key, value, next);
}
我們接下來看下Node類的代碼
/**
* 該類只實現了 Map.Entry 接口,
* 所以該類只需要實現getKey、getValue、setValue三個方法即可
* 除此之外以什麼樣的方式來組織數據,就和接口無關了
*/
static class Node<K,V> implements Map.Entry<K,V> {
final int hash; // hash值,不可變
final K key; // 鍵,不可變
V value; // 值
Node<K,V> next; // 下一個節點
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
// 實現接口定義的方法,且該方法不可被重寫
public final K getKey() { return key; }
// 實現接口定義的方法,且該方法不可被重寫
public final V getValue() { return value; }
public final String toString() { return key + "=" + value; }
// 重寫父類Object的hashCode方法,且該方法不可被自己的子類再重寫
// 返回:key的hashCode值和value的hashCode值進行異或運算結果
public final int hashCode() {
return Objects.hashCode(key) ^ Objects.hashCode(value);
}
// 實現接口定義的方法,且該方法不可被重寫
// 設值,返回舊值
public final V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
/*
* 重寫父類Object的equals方法,且該方法不可被自己的子類再重寫
* 判斷相等的依據是,只要是Map.Entry的一個實例,並且鍵鍵、值值都相等就返回True
*/
public final boolean equals(Object o) {
if (o == this)
return true;
if (o instanceof Map.Entry) {
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
if (Objects.equals(key, e.getKey()) &&
Objects.equals(value, e.getValue()))
return true;
}
return false;
}
}
三、小貼士
在上一個小節中存在着Objects.equals、Objects.hashCode這種靜態方法,其內部實現都特別簡單。
以下是截取Objects類中這兩個方法的實現。
public static int hashCode(Object o) {
return o != null ? o.hashCode() : 0;
}
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
最主要的幫助用戶節省了空值判斷和指針相同判斷。