6.3 HashMap 內部類 KeySet、Values、EntrySet
阿新 • • 發佈:2018-12-12
KeySet、Values、EntrySet 操作其實都差不多,其內部並不儲存任何資料,這三個內部內都是封裝了方便外部對於 table 的 key,value,node 的遍歷和操作
一、KeySet
是 HashMap 中所有 key 的集合
// HashMap 的 table 中所有 key 的集合
final class KeySet extends AbstractSet<K> {
// 獲取到的其實是 table 的元素個數,並不是 key 的個數,不過其實也一樣
public final int size() { return size; }
// 呼叫的外部的 clear(),所以呼叫這個方法會清空 table 中的資料,
// 裡面的原理其實不是 table 引用重指向,而是 table 中的桶賦空
public final void clear() { HashMap.this.clear(); }
// 返回一個用於迭代 KeySet 的迭代器
public final Iterator<K> iterator() { return new KeyIterator(); }
// 呼叫外部 containsKey(o) 方法,用於查詢 table 是否存在 key 為 o 的元素
public final boolean contains(Object o) { return containsKey(o); }
// 根據 key 移除元素,同樣和對外暴露的 remove(Object key),功能一樣
public final boolean remove(Object key) {
return removeNode(hash(key), key, null, false, true) != null;
}
// 單看方法名的話就是一個分割迭代器了,同樣用於迭代,但是分割了,所以可以並行迭代,具體操作後面關於並行迭代器再說~~
public final Spliterator<K> spliterator() {
return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0);
}
// jdk1.8 中出現的函數語言程式設計,裡面的引數是一個函式式介面,
// 也就是說可以傳入該介面的實現,也可以傳入一個 Lambda 表示式(箭頭函式)
public final void forEach(Consumer<? super K> action) {
Node<K,V>[] tab;
if (action == null)
throw new NullPointerException();
if (size > 0 && (tab = table) != null) {
int mc = modCount;
for (int i = 0; i < tab.length; ++i) {
for (Node<K,V> e = tab[i]; e != null; e = e.next)
action.accept(e.key);
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
}
二、Values
是 HashMap 中所有 value 的集合
//HashMap 的 table 中所有 value 的集合
final class Values extends AbstractCollection<V> {
// 獲取到的其實是 table 的元素個數,並不是 value 的個數,不過其實也一樣
public final int size() { return size; }
// 和 KeySet 的功能一樣
public final void clear() { HashMap.this.clear(); }
// 獲取一個用於迭代 value 的迭代器
public final Iterator<V> iterator() { return new ValueIterator(); }
// 和 table 對外暴露的 containsValue(Object value),方法一樣,判斷 table 中是否存在值為 o 的元素
public final boolean contains(Object o) { return containsValue(o); }
// 獲取一個 value 的分割迭代器,功能和操作類似 KeySet
public final Spliterator<V> spliterator() {
return new ValueSpliterator<>(HashMap.this, 0, -1, 0, 0);
}
// 功能和操作類似 KeySet
public final void forEach(Consumer<? super V> action) {
Node<K,V>[] tab;
if (action == null)
throw new NullPointerException();
if (size > 0 && (tab = table) != null) {
int mc = modCount;
for (int i = 0; i < tab.length; ++i) {
for (Node<K,V> e = tab[i]; e != null; e = e.next)
action.accept(e.value);
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
}
三、EntrySet
EntrySet 就不解釋了,原理都一樣,細節稍有不同,這三者較大的不同點在於 KeySet 和 EntrySet 繼承於 AbstractSet,Values 繼承於 AbstractCollection,這個就和裡面的值能否重複有關了,KeySet 和 EntrySet 不允許重複,Values 允許有相同
另外一個要注意的是利用 EntrySet 移除節點時,會對 table 的元素進行重排,慎用~