Java中Map介面及實現
Map介面及實現:
1. Map介面
Map是從鍵到值的對映,鍵不允許重複,每個鍵最多能對映一個值.
public interface Map<K,V> {
// Query Operations
int size();
boolean isEmpty();
boolean containsKey(Object key);
boolean containsValue(Object value);
V get(Object key);
// Modification Operations
V put(K key, V value);
V remove(Object key);
// Bulk Operations
void putAll(Map<? extends K, ? extends V> t);
void clear();
// Views
Set<K> keySet();
Collection<V> values();
Set<Map.Entry<K, V>> entrySet();
interface Entry<K,V> {
K getKey();
V getValue();
V setValue(V value);
boolean equals(Object o);
int hashCode();
}
// Comparison and hashing
boolean equals(Object o);
int hashCode();
}
Entry介面及實現
Map是鍵到值的對映,Java集合框架在實現上採用一個個Map.Entry來封裝每一個鍵值對,這樣,Map中的元素就變成了Map.Entry的集了,這似乎預示著Map和Set有某種相通性.實際上,在HashSet實現中,HashSet的實現中,HashSet的元素儲存在底層就是藉助於HashMap來進行的(在部分情況下使用了LinkedHashMap).
//AbstractMap中的SimpleEntry類
static class SimpleEntry<K,V> implements Entry<K,V> {
K key;
V value;
public SimpleEntry(K key, V value) {
this.key = key;
this.value = value;
}
public SimpleEntry(Entry<K,V> e) {
this.key = e.getKey();
this.value = e.getValue();
}
public K getKey() {
return key;
}
public V getValue() {
return value;
}
public V setValue(V value) {
V oldValue = this.value;
this.value = value;
return oldValue;
}
public boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry e = (Map.Entry)o;
return eq(key, e.getKey()) && eq(value, e.getValue());
}
public int hashCode() {
return ((key == null) ? 0 : key.hashCode()) ^
((value == null) ? 0 : value.hashCode());
}
public String toString() {
return key + "=" + value;
}
private static boolean eq(Object o1, Object o2) {
return (o1 == null ? o2 == null : o1.equals(o2));
}
}
2.Map的實現
在java 2集合框架中的Map介面有兩個通用實現:HashMap和TreeMap. HashMap是採用雜湊表實現,是Map介面的最好的全面實現.TreeMap實現了Map的子介面SortedMap,採用紅黑樹作為底層儲存結構,提供了按照鍵排序的Map儲存.
2.1HashMap和Hashtable的對比:
HashMap Hashtable
鍵和值是否允許空 允許 不允許
是否多執行緒同步 否 是
但是HashMap可以通過如下方式方便地得到HashMap的一個同步檢視:
Map synMap = Collections.synchronizedMap (new HashMap(...));
Hashtable已經經過再造,這是為了保持向後的相容,保證老版本的程式能夠繼續正常允許;同時也方便Hashtable和新集合框架進行互操作(新的Hashtable實現了Map介面).正是因為Hashtable經過這些修改,反而使得它混合了新舊的很多特性,過於複雜,臃腫.
新的Map介面採用了比原來Hashtable採用的Enumeration功能更加強大的Iterator遍歷工具.
Enumeration現在也不推薦使用, 不僅是因為Enumeration沒有提供遍歷鍵或值時安全刪除Hashtable中的元素的途徑;並且Enumeration的名稱也不符合Sun的Java命名規範(採用Enumeration更加合適).而Hashtable為了相容兩者,在實現時,不得不提供同時實現了Enumeration和Iterator介面的Enumeration內部類.所以推薦大家用HashMap.
2.2 TreeMap
TreeMap中的Tree是一顆平衡二叉樹,即每個內部節點都有一個前導節點或者說或者說父節點和兩個子節點.儲存後繼節點的那個成員被命名為left和right. 二叉樹在平衡時或者葉子節點到根結點的高度在一定的範圍內時工作起來是最有效的.平衡一個二叉樹的原因是為了縮短從根結點(這是在 TreeMap類中惟一能夠被直接引用的結點)至每個葉子節點的距離.距離越短,訪問那個節點所需時間就會越少.平衡樹的定義因實現而異.java2 集合框架中的樹採用紅黑樹.紅黑樹是一種平衡樹,這個平衡樹的定義是沒有一個葉子節點跟其他葉子節點的深度差超過兩步.這個名字就是由於樹的每個節點都被著上紅色和黑色,節點所著的顏色被用來檢測樹的平衡性.在對節點插入和刪除的操作中,可能會被旋轉來保持樹的平衡性.一般和最壞情況插入,刪除,查詢時間都是O(lgn).
TreeMap的put方法:
public V put(K key, V value) {
Entry<K,V> t = root;
if (t == null) {
incrementSize();
root = new Entry<K,V>(key, value, null);
return null;
}
while (true) {
int cmp = compare(key, t.key);
if (cmp == 0) {
return t.setValue(value);
} else if (cmp < 0) {
if (t.left != null) {
t = t.left;
} else {
incrementSize();
t.left = new Entry<K,V>(key, value, t);
fixAfterInsertion(t.left);
return null;
}
} else { // cmp > 0
if (t.right != null) {
t = t.right;
} else {
incrementSize();
t.right = new Entry<K,V>(key, value, t);
fixAfterInsertion(t.right);
return null;
}
}
}
}
2.3 Map實現的選用
在不需要有序儲存時,可以採用高效的HashMap;如果需要有序儲存,可以採用TreeMap,但是由於TreeMap是以紅黑樹進行儲存的,需要比HashMap更多的空間和時間的開銷.