- Map接口映射key到value。一個map是不允許包含重複的key,一個key最多可以映射到一個值。
- Map接口提供三種集合視圖,一個map可以包含一個key值的set集合,一個value值的Collection集合,和一個key-value值的set集合。
- 一個通用的Map應該實現兩類構造器:一類是不帶參數的構造器;一類是指定key-value類型的構造器。
- Entry<K,V>一個map的entry,鍵值對(key-value)。可以通過Map的entrySet()返回該Map的Entry<K,V>。
public abstract Set<Entry<K,V>> entrySet();
transient volatile Set<K> keySet;//保存key值的set集合
transient volatile Collection<V> values;//保存value值的Collection集合
/*
*該實現返回一個AbstractSet的子類,該子類的迭代器iterator方法是由該
* 集合的entrySet返回的迭代器的相應方法實現的。
*
*/
public Set<K> keySet() {
if (keySet == null) {
keySet = new AbstractSet<K>() {
public Iterator<K> iterator() {
return new Iterator<K>() {
private Iterator<Entry<K,V>> i = entrySet().iterator();//返回entrySet的迭代器
public boolean hasNext() {
return i.hasNext();
}
public K next() {
return i.next().getKey();//獲取值元素
}
public void remove() {
i.remove();
}
};
}
public int size() {
return AbstractMap.this.size();
}
public boolean isEmpty() {
return AbstractMap.this.isEmpty();
}
public void clear() {
AbstractMap.this.clear();
}
public boolean contains(Object k) {
return AbstractMap.this.containsKey(k);
}
};
}
return keySet;
}
/*
* 該實現返回一個AbstractCollection的子類,子類的iterator方法是由該
* 集合的entrySet返回的迭代器的相應方法實現的。
*
*/
public Collection<V> values() {
if (values == null) {
values = new AbstractCollection<V>() {
public Iterator<V> iterator() {
return new Iterator<V>() {
private Iterator<Entry<K,V>> i = entrySet().iterator();//返回entrySet的迭代器
public boolean hasNext() {
return i.hasNext();
}
public V next() {
return i.next().getValue();
}
public void remove() {
i.remove();
}
};
}
public int size() {
return AbstractMap.this.size();
}
public boolean isEmpty() {
return AbstractMap.this.isEmpty();
}
public void clear() {
AbstractMap.this.clear();
}
public boolean contains(Object v) {
return AbstractMap.this.containsValue(v);
}
};
}
return values;
}
/*
* 一個Map entry,鍵值對。Map的entrySet()方法返回一個Set集合的entry。
* 獲取一個map的鍵值對entry,只能通過Set集合的迭代器纔可以獲取到,即
* 通過entrySet().iterator()方法獲取到迭代器,然後再獲取Entry<K,V>。
*
*/
interface Entry<K,V> {
K getKey();
V getValue();
V setValue(V value);
boolean equals(Object o);
int hashCode();
}
- HashMap實現了Map接口,HashMap提供了所有可選map操作,允許添加null value和null key。
- HashMap基本上等同於Hashtable,除了HashMap不支持同步,並且允許添加null元素。
- HashMap不保證元素在集合中的順序。
- HashMap的一些基本操作,例如get、put操作,可以控制在常數時間內。迭代操作的性能主要受HashMap的容量和元素個數的多少影響。
- HashMap集合有兩個參數影響它的性能:初始化容量capability和加載因子load factor。capability代表在hash table中卡槽的數量;加載因子是一個衡量集合被填滿的程度,用來指示容量被自動增長前可以填充最多的元素。當HashMap中元素的數量超過了加載因子所允許的最大值,HashMap將增加一半的容量,並重新映射元素。因此,在初始化HashMap的時候,不要把capability設置的過大或者將load factor設置的過小,如果迭代性能很重要的話。
- 作爲一個通用的規則,默認的加載因子設置爲0.75,提供了一個在時間和空間耗費上的平衡。
- HashMap不是一個同步的集合,如果多個線程同時操作該集合的,需要有同步操作。可以通過以下方法來創建一個同步集合Collections.synchronizedMap,例如:
- HashMap默認初始化的容量capability是16,最大容量是1<<30。默認的加載因子是0.75。
/*
* 返回該Map映射的key-value集合,該Set集合是被Map共享的,
* 任何在該Set上的修改,都會反應到該Map中。
*/
public Set<Map.Entry<K,V>> entrySet() {
Set<Map.Entry<K,V>> es;
return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
}
接下來看EntrySet的實現
final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
public final int size() { return size; }
public final void clear() { HashMap.this.clear(); }
public final Iterator<Map.Entry<K,V>> iterator() {
return new EntryIterator();//返回Entry的迭代器
}
public final boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>) o;
Object key = e.getKey();
Node<K,V> candidate = getNode(hash(key), key);
return candidate != null && candidate.equals(e);
}
public final boolean remove(Object o) {
if (o instanceof Map.Entry) {
Map.Entry<?,?> e = (Map.Entry<?,?>) o;
Object key = e.getKey();
Object value = e.getValue();
return removeNode(hash(key), key, value, true, true) != null;
}
return false;
}
}
EntrySet繼承了AbstractSet,並且迭代器方法iterator返回了一個EntryIterator。EntryIterator的實現如下:
//繼承自HashIterator
final class EntryIterator extends HashIterator
implements Iterator<Map.Entry<K,V>> {
public final Map.Entry<K,V> next() { return nextNode(); }
}
EntryIterator繼承自HashIterator,並且實現了迭代器的接口Iterator。HashIterator的實現如下:
abstract class HashIterator {
Node<K,V> next; // next entry to return
Node<K,V> current; // current entry
int expectedModCount; // for fast-fail
int index; // current slot
HashIterator() {
expectedModCount = modCount;
Node<K,V>[] t = table;//保存entry的數組
current = next = null;
index = 0;
if (t != null && size > 0) { // advance to first entry
do {} while (index < t.length && (next = t[index++]) == null);
}
}
public final boolean hasNext() {
return next != null;
}
final Node<K,V> nextNode() {
Node<K,V>[] t;
Node<K,V> e = next;
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
if (e == null)
throw new NoSuchElementException();
//尋找下一個節點
if ((next = (current = e).next) == null && (t = table) != null) {
do {} while (index < t.length && (next = t[index++]) == null);
}
return e;
}
public final void remove() {
Node<K,V> p = current;
if (p == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
current = null;
K key = p.key;
removeNode(hash(key), key, null, false, false);
expectedModCount = modCount;
}
}
table爲保存Node節點的數組,首次使用時初始化,必要時重新定義大小。定義如下: transient Node<K,V>[] table;
/*
* hash方法是求key值對於的hash值,並且將hash高16位與低16位進行XOR(異或)操作,作爲key值最終的hash值。
* 這樣做的目的是爲了減少衝突
*/
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
在HashIterator的remove方法調用了removeNode()方法,接下來看下removeNode()方法的實現:/*
* 實現Map.remove和相關方法
* HashMap採用的是鏈地址法解決衝突,而且採用的是單鏈表
* hash key值的hash值
* value 需要匹配的value值,如果matchValue爲true的話,否則忽略
* matchValue 如果爲true的話,則只刪除value相等的元素
* movable 如果爲false的話,在刪除的時候,不移動其他元素
*/
final Node<K,V> removeNode(int hash, Object key, Object value,
boolean matchValue, boolean movable) {
Node<K,V>[] tab; Node<K,V> p; int n, index;
if ((tab = table) != null && (n = tab.length) > 0 &&
(p = tab[index = (n - 1) & hash]) != null) {//節點p是開始查找的起始節點
Node<K,V> node = null, e; K k; V v;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
node = p;//節點p就是所要查找的節點
else if ((e = p.next) != null) {
if (p instanceof TreeNode)
node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
else {
do {//循環查詢鏈表,直到查找到hash值和key值符合的節點
if (e.hash == hash &&
((k = e.key) == key ||
(key != null && key.equals(k)))) {
node = e;
break;
}
p = e;//保存當前節點
} while ((e = e.next) != null);
}
}
//找到了需要刪除的節點
if (node != null && (!matchValue || (v = node.value) == value ||
(value != null && value.equals(v)))) {
if (node instanceof TreeNode)
((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
else if (node == p)
tab[index] = node.next;// 刪除的節點是頭結點
else
p.next = node.next; // 刪除的節點是中間節點
++modCount;
--size;
afterNodeRemoval(node);
return node;
}
}
return null;
}
/*
* 實現Map的get和相關的方法
* hash key值的散列碼
* key key值
*/
final Node<K,V> getNode(int hash, Object key) {
Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) {
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k))))
return first;
if ((e = first.next) != null) {
if (first instanceof TreeNode)
return ((TreeNode<K,V>)first).getTreeNode(hash, key);
do {
if (e.hash == hash && // 找出key和hash都符合的node
((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
}
return null;
}
/*
* 實現Map的put和相關方法
* hash key的hash值
* key key值
* value 需要保存的value值
* onlyIfAbsent 如果爲true,則不改變已經存在的值
* evict 如果爲false,則table處於創建模式
*/
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;//table爲空,重新創建一個table
if ((p = tab[i = (n - 1) & hash]) == null)//如果table中hash值對應的第一個值爲空,創建一個新的node節點
tab[i] = newNode(hash, key, value, null);
else {//先查找是否已經存在對應key值的node節點
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;//找到了頭結點與key值相等
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);//如果沒有找到節點與key值相等,則在鏈表的尾部添加一個新的節點
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;//找到了中間某個節點與key值相等
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
//如果onlyIfAbssent爲空或者oldValue值爲空時,才更新value值,並返回oldValue,否則的話,返回null
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
// 如果size大小超出了HashMap的容量的臨界值,則擴展HashMap的容量threshold = capability*load factor。
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
/*
* 初始化或者加倍table的大小,如果爲table爲空,則根據初始容量threshold來分配。
* 另外,由於table的大小是兩倍的擴展,擴展後容器內的元素要麼保存在原來的位置,要麼保存在原來位置的兩倍偏移位置。
*/
final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table;
int oldCap = (oldTab == null) ? 0 : oldTab.length;
int oldThr = threshold;
int newCap, newThr = 0;
if (oldCap > 0) {
if (oldCap >= MAXIMUM_CAPACITY) {//舊錶的容量已經超出了最大值,則直接返回
threshold = Integer.MAX_VALUE;
return oldTab;
}
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY) //舊錶的容量大於默認初始化容量16,新表的容量是舊錶容量的2倍。
newThr = oldThr << 1; // double threshold
}
else if (oldThr > 0) // initial capacity was placed in threshold
newCap = oldThr;
else { // zero initial threshold signifies using defaults
newCap = DEFAULT_INITIAL_CAPACITY;// 16
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);// 16*0.75 = 12
}
// 如果新的table臨界值爲0的話,則通過新表格的容量乘以加載因子得出新的臨界值
if (newThr == 0) {
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
threshold = newThr;
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];//創建一個新table數組
table = newTab;
//將舊錶oldTab元素拷貝到新表newTable中
if (oldTab != null) {
for (int j = 0; j < oldCap; ++j) {
Node<K,V> e;
if ((e = oldTab[j]) != null) {
oldTab[j] = null;//釋放舊錶保存的node節點
if (e.next == null)
newTab[e.hash & (newCap - 1)] = e;//如果卡槽j處只有一個node節點, 則直接保存到新表數組中
else if (e instanceof TreeNode)
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
else { // preserve order
// table擴展容量後,可以分爲兩部分,一部分還是原來的那部分,用loHead和loTail表示
// 新增的那一部分,用hiHead和hiTial來表示
// 舊錶格的元素要麼保存在原來的那部分位置,要麼被保存在新添加的那部分,他們之間相差一個capability。
Node<K,V> loHead = null, loTail = null;
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;
do {
next = e.next;//鏈表中下一個節點
// 保存在舊錶格中節點,通過loHead和loTail來指定需要保存的鏈表
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}// 保存在新表格中的節點,通過hiHead和hiTail來指定需要保存的鏈表
else {
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead;//在新表的低地址部分添加
}
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;//在新表的高地址部分添加
}
}
}
}
}
return newTab;
}
/*
* Node節點實現Map的Entry
* next指向下一個Node節點,維護的是單鏈表結構
*/
static class Node<K,V> implements Map.Entry<K,V> {
final int 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; }
public final int hashCode() {
return Objects.hashCode(key) ^ Objects.hashCode(value);
}
public final V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
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;
}
}
- LinkedHashMap是一個實現了Map接口的哈希鏈表。
- LinkedHashMap不同於HashMap,它維護一個雙向鏈接鏈表。迭代器返回的元素順序就是元素插入時的順序。
- LinkedHashMap可以被用來產生Map集合的一份拷貝,並且元素的順序保持不變,不管原始的map實現是什麼,例如:
- LinkedHashMap集合允許添加null元素,像HashMap一樣,LinkedHashMap在常數時間內完成一些基本的操作,例如add、contains、remove等操作。LinkedHashMap的性能要稍微低於HashMap,因爲LinkedHashMap需要維護一個鏈表。
- LinkedHashMap有兩個因素影響它的性能:初始容量capability、加載因子Load Factor。
- LinkedHashMap方法不是同步的,如果是多個線程使用的話,需要同步。可以通過Collections.synchronizedMap方法來獲取同步的容器。例如:
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before, after;
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}
在LinkedHashMap中還保存了雙向鏈表的頭entry指針和尾entry指針。
// 保存雙向鏈表的頭Entry,存留時間最久的節點
transient LinkedHashMap.Entry<K,V> head;
// 保存雙向鏈表的尾Entry,存留時間最短的節點
transient LinkedHashMap.Entry<K,V> tail;
LinkedHashMap的雙鏈表結構如下圖所示:final boolean accessOrder;// 該變量決定是以訪問順序遍歷還是以插入順序遍歷,如果爲true則以訪問順序遍歷,否則則以插入順序遍歷。
LinkedHashMap是繼承自HashMap,構造函數最終還是會調用到LinkedHashMap。//LinkedHashMap最終調用的還是HashMap的構造函數
public LinkedHashMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
accessOrder = false;
}
LinkedHashMap的entrySet方法實現如下:/*
* 返回該Map的key-value集合
*/
public Set<Map.Entry<K,V>> entrySet() {
Set<Map.Entry<K,V>> es;
return (es = entrySet) == null ? (entrySet = new LinkedEntrySet()) : es;
}
final class LinkedEntrySet extends AbstractSet<Map.Entry<K,V>> {
public final int size() { return size; }
public final void clear() { LinkedHashMap.this.clear(); }
public final Iterator<Map.Entry<K,V>> iterator() {
return new LinkedEntryIterator();//返回LinkedEntryIterator迭代器
}
public final boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>) o;
Object key = e.getKey();
Node<K,V> candidate = getNode(hash(key), key);//調用HashMap的getNode去獲取key對於的Node節點
return candidate != null && candidate.equals(e);
}
public final boolean remove(Object o) {
if (o instanceof Map.Entry) {
Map.Entry<?,?> e = (Map.Entry<?,?>) o;
Object key = e.getKey();
Object value = e.getValue();
return removeNode(hash(key), key, value, true, true) != null;
}
return false;
}
}
final class LinkedEntryIterator extends LinkedHashIterator
implements Iterator<Map.Entry<K,V>> {
public final Map.Entry<K,V> next() { return nextNode(); }
}
abstract class LinkedHashIterator {
LinkedHashMap.Entry<K,V> next;//指向下一個Entry
LinkedHashMap.Entry<K,V> current;//指向當前Entry
int expectedModCount;
LinkedHashIterator() {
next = head;//指向頭Entry
expectedModCount = modCount;
current = null;
}
public final boolean hasNext() {
return next != null;
}
final LinkedHashMap.Entry<K,V> nextNode() {
LinkedHashMap.Entry<K,V> e = next;
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
if (e == null)
throw new NoSuchElementException();
current = e;//保存當前Entry到current
next = e.after;//next指向下一個Entry
return e;
}
public final void remove() {
Node<K,V> p = current;
if (p == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
current = null;
K key = p.key;
removeNode(hash(key), key, null, false, false);//調用HashMap的removeNode移除節點
expectedModCount = modCount;
}
}
LinkedHashMap有自己的get方法實現,如果設置了按訪問順序迭代元素,則還需要調用HashMap的鉤子函數afterNodeAccess。/*
* 獲取key值對應的value,value可能爲null
*/
public V get(Object key) {
Node<K,V> e;
if ((e = getNode(hash(key), key)) == null)
return null;
// 如果設置了以訪問順序迭代元素,則調用afterNodeAccess鉤子函數
// 該函數在HashMap中定義了。
if (accessOrder)
afterNodeAccess(e);
return e.value;
}
/*
* 將被訪問過的Node移動到最後,在HashMap中定義該鉤子函數
*/
void afterNodeAccess(Node<K,V> e) { // move node to last
LinkedHashMap.Entry<K,V> last;
if (accessOrder && (last = tail) != e) {//節點e不是最後的節點
LinkedHashMap.Entry<K,V> p =
(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;//p保存e節點,b保存e的前一個節點,a保存的是e的下一個節點
p.after = null;
if (b == null)// e的前一個節點爲空,則說明e是頭結點
head = a;// 將頭結點保存爲e的下一個節點
else
b.after = a;//節點e從鏈表中被移除
if (a != null)
a.before = b;
else
last = b;//e的下一個節點爲空,則說明e是尾節點,將尾節點指向e節點的前一個節點
if (last == null)
head = p;
else {
p.before = last;
last.after = p;//將e節點鏈接到鏈表的尾部
}
tail = p;//更新tail指針
++modCount;
}
}
可以看到afterNodeAccess函數只做了一件事,將訪問過的節點移至雙向鏈表的尾部,這樣可以以訪問順序來迭代該集合。 LinkedHashMap還有幾個其他的HashMap鉤子函數實現,分別是afterNodeInsertion()、afterNodeRemoval()、newNode(),分別在插入元素、刪除元素、創建新的元素時回調。
/*
* 在插入元素後,回調該方法,在HashMap中定義
* evict
*/
void afterNodeInsertion(boolean evict) { // possibly remove eldest
LinkedHashMap.Entry<K,V> first;
if (evict && (first = head) != null && removeEldestEntry(first)) {//如果需要移除最近最少訪問的元素
K key = first.key;
removeNode(hash(key), key, null, false, true);//調用removeNode方法移除該Node
}
}
/*
* 如果返回true的話,將移除保存最久的元素。
* 該方法在調用put或者putAll方法插入一個新元素時調用。
* 這樣可以讓實現者有機會在添加新元素的時候,可以刪除保留最久的元素。
* 這種場景非常適合用於cache,通過刪除過時的entry來減少內存消耗。
* 一個簡單的例子,覆寫該方法,並且設定最大保留的緩存數量爲100,這樣當超過100後,就會刪除一些元素。
* private static final int MAX_ENTRIES = 100;
* protected boolean removeEldestEntry(Map.Entry eldest) {
* return size() > MAX_ENTRIES;
* }
* eldest 最近最少訪問的entry,如果該方法返回true,則該entry將被刪除。
*/
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
return false;
}
/*
* 在刪除元素後,回調該方法,在HashMap中定義
*/
void afterNodeRemoval(Node<K,V> e) { // unlink
LinkedHashMap.Entry<K,V> p =
(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
p.before = p.after = null;//斷開節點e到前一個節點和後一個節點的鏈接
if (b == null)//e的前一個節點爲空,則說明e是頭結點
head = a;//將頭結點head指向e的下一個節點
else
b.after = a;
if (a == null)//e的下一個節點爲空,則說明e是尾節點
tail = b;//將尾節點tail指向e的前一個節點
else
a.before = b;
}
/*
* 創建一個新的節點,在HashMap中定義了該方法
*/
Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
LinkedHashMap.Entry<K,V> p =
new LinkedHashMap.Entry<K,V>(hash, key, value, e);
linkNodeLast(p);//將節點鏈接到鏈表的尾部
return p;
}
// link at the end of list
private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
LinkedHashMap.Entry<K,V> last = tail;
tail = p;
if (last == null)
head = p;
else {
p.before = last;
last.after = p;
}
}
LinkedEntrySet類圖如下圖所示:- TreeMap<K,V>底層是基於紅黑樹實現的。
- TreeMap是一個有序的Map,根據關鍵字key的Comparable屬性排序或者外部提供的Comparator比較排序。
- TreeMap能在log(n)時間內執行完containsKey、get、put、remove等操作。
- TreeMap通過compareTo()或者compare()方法,執行所有的元素比較操作。
- TreeMap集合不是同步的,如果是多線程操作,必須提供相應的同步。可以通過Collections.synchronizedSortedMap方法創建一個同步的容器。例如:
//在TreeMap中保存的節點
static final class Entry<K,V> implements Map.Entry<K,V> {
K key;//保存key鍵值
V value;//保存value值
Entry<K,V> left;//左節點
Entry<K,V> right;//右節點
Entry<K,V> parent;//雙親節點
boolean color = BLACK;//紅黑樹的顏色
Entry(K key, V value, Entry<K,V> parent) {
this.key = key;
this.value = value;
this.parent = parent;
}
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 valEquals(key,e.getKey()) && valEquals(value,e.getValue());
}
public int hashCode() {
int keyHash = (key==null ? 0 : key.hashCode());
int valueHash = (value==null ? 0 : value.hashCode());
return keyHash ^ valueHash;
}
}
- 默認無參構造器;創建一個空的排序Map,排序規則根據元素自帶的比較屬性。
- 帶一個Comparator的構造器;創建一個空的排序Map,元素的排序規則根據Comparator來決定。
- 帶一個Map類型參數的構造器;創建一個新的排序Map,並且包含參數Map中的元素和排序規則。
- 帶一個SortedMap參數的構造器;創建一個新的排序Map,並且包含參數SortedMap的元素和排序順序。
HashMap(默認的選擇)
|
Map基於散列表的實現。在插入和查詢“鍵值對”的開銷是固定的。可以通過構造器設置容量和負載因子,以調整容器的性能。
|
LinkedHashMap
|
類似於HashMap,但是迭代遍歷它時,取得“鍵值對”的順序是其插入的次序,或者是最近最少使用LRU的次序,只比HashMap慢一點,而在迭代訪問時反而更快,因爲它使用鏈表維護內部次序。 |
TreeMap
|
基於紅黑樹的實現。查看“鍵”或者“鍵值對”時,他們會被排序(次序由Comparator或者Comparable決定)。TreeMap的特點在於,所得到的結果是經過排序的。TreeMap是唯一的帶有subMap()方法的Map,它可以返回一個子樹。
|
WeakHashMap
|
弱鍵映射,允許釋放映射所指的對象;這是解決某類特殊問題而設計的。如果映射之外沒有引用指向某個“鍵”,則此“鍵”可以被垃圾收集器回收。
|
ConcurrentHashMap
|
一種線程安全的Map,它不涉及同步加鎖。
|