Java 集合深入理解(15):AbstractMap
今天來了解下 AbstractMap。
什麼是 AbstractMap
AbstractMap 是 Map 介面的的實現類之一,也是 HashMap, TreeMap, ConcurrentHashMap 等類的父類。
AbstractMap 提供了 Map 的基本實現,使得我們以後要實現一個 Map 不用從頭開始,只需要繼承 AbstractMap, 然後按需求實現/重寫對應方法即可。
AbstarctMap 中唯一的抽象方法:
public abstract Set<Entry<K,V>> entrySet();
當我們要實現一個 不可變
entrySet()
方法,這個方法返回一個儲存所有 key-value 對映的 set。 通常這個 Set 不支援 add(), remove() 方法,Set 對應的迭代器也不支援 remove() 方法。
如果想要實現一個 可變的 Map,我們需要在上述操作外,重寫 put() 方法,因為 預設不支援 put 操作:
public V put(K key, V value) {
throw new UnsupportedOperationException();
}
而且 entrySet() 返回的 Set 的迭代器,也得實現 remove() 方法,因為 AbstractMap 中的 刪除相關操作都需要呼叫該迭代器的 remove() 方法。
- 一種是不含引數的,返回一個空 map
- 一種是以一個 map 為引數,返回一個和引數內容一樣的 map
AbstractMap 的成員變數
transient volatile Set<K> keySet;
transient volatile Collection<V> values;
有兩個成員變數:
- keySet, 儲存 map 中所有鍵的 Set
- values, 儲存 map 中所有值的集合
他們都是 transient, volatile, 分別表示不可序列化、併發環境下變數的修改能夠保證執行緒可見性。
需要注意的是 volatile 只能保證可見性,不能保證原子性,需要保證操作是原子性操作,才能保證使用 volatile 關鍵字的程式在併發時能夠正確執行。
AbstractMap 的成員方法
AbstractMap 中實現了許多方法,實現類會根據自己不同的要求選擇性的覆蓋一些。
接下來根據看看 AbstractMap 中的方法。
1.新增
public V put(K key, V value) {
throw new UnsupportedOperationException();
}
public void putAll(Map<? extends K, ? extends V> m) {
for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
put(e.getKey(), e.getValue());
}
可以看到預設是不支援新增操作的,實現類需要重寫 put() 方法。
2.刪除
public V remove(Object key) {
//獲取儲存 Map.Entry 集合的迭代器
Iterator<Entry<K,V>> i = entrySet().iterator();
Entry<K,V> correctEntry = null;
//遍歷查詢,當某個 Entry 的 key 和 指定 key 一致時結束
if (key==null) {
while (correctEntry==null && i.hasNext()) {
Entry<K,V> e = i.next();
if (e.getKey()==null)
correctEntry = e;
}
} else {
while (correctEntry==null && i.hasNext()) {
Entry<K,V> e = i.next();
if (key.equals(e.getKey()))
correctEntry = e;
}
}
//找到了,返回要刪除的值
V oldValue = null;
if (correctEntry !=null) {
oldValue = correctEntry.getValue();
//呼叫迭代器的 remove 方法
i.remove();
}
return oldValue;
}
//呼叫 Set.clear() 方法清除
public void clear() {
entrySet().clear();
}
3.獲取
//時間複雜度為 O(n)
//許多實現類都重寫了這個方法
public V get(Object key) {
//使用 Set 迭代器進行遍歷,根據 key 查詢
Iterator<Entry<K,V>> i = entrySet().iterator();
if (key==null) {
while (i.hasNext()) {
Entry<K,V> e = i.next();
if (e.getKey()==null)
return e.getValue();
}
} else {
while (i.hasNext()) {
Entry<K,V> e = i.next();
if (key.equals(e.getKey()))
return e.getValue();
}
}
return null;
}
4.查詢狀態
//是否存在指定的 key
//時間複雜度為 O(n)
//許多實現類都重寫了這個方法
public boolean containsKey(Object key) {
//還是迭代器遍歷,查詢 key,跟 get() 很像啊
Iterator<Map.Entry<K,V>> i = entrySet().iterator();
if (key==null) {
while (i.hasNext()) {
Entry<K,V> e = i.next();
//getKey()
if (e.getKey()==null)
return true;
}
} else {
while (i.hasNext()) {
Entry<K,V> e = i.next();
if (key.equals(e.getKey()))
return true;
}
}
return false;
}
//查詢是否存在指定的值
public boolean containsValue(Object value) {
Iterator<Entry<K,V>> i = entrySet().iterator();
if (value==null) {
while (i.hasNext()) {
Entry<K,V> e = i.next();
//getValue()
if (e.getValue()==null)
return true;
}
} else {
while (i.hasNext()) {
Entry<K,V> e = i.next();
if (value.equals(e.getValue()))
return true;
}
}
return false;
}
public int size() {
//使用 Set.size() 獲取元素個數
return entrySet().size();
}
public boolean isEmpty() {
return size() == 0;
}
5.用於比較的 equals(), hashCode()
//內部用來測試 SimpleEntry, SimpleImmutableEntry 是否相等的方法
private static boolean eq(Object o1, Object o2) {
return o1 == null ? o2 == null : o1.equals(o2);
}
//判斷指定的物件是否和當前 Map 一致
//為什麼引數不是泛型而是 物件呢
//據說是建立這個方法時還沒有泛型 - -
public boolean equals(Object o) {
//引用指向同一個物件
if (o == this)
return true;
//必須是 Map 的實現類
if (!(o instanceof Map))
return false;
//強轉為 Map
Map<?,?> m = (Map<?,?>) o;
//元素個數必須一致
if (m.size() != size())
return false;
try {
//還是需要一個個遍歷,對比
Iterator<Entry<K,V>> i = entrySet().iterator();
while (i.hasNext()) {
//對比每個 Entry 的 key 和 value
Entry<K,V> e = i.next();
K key = e.getKey();
V value = e.getValue();
if (value == null) {
//對比 key, value
if (!(m.get(key)==null && m.containsKey(key)))
return false;
} else {
if (!value.equals(m.get(key)))
return false;
}
}
} catch (ClassCastException unused) {
return false;
} catch (NullPointerException unused) {
return false;
}
return true;
}
//整個 map 的 hashCode()
public int hashCode() {
int h = 0;
//是所有 Entry 雜湊值的和
Iterator<Entry<K,V>> i = entrySet().iterator();
while (i.hasNext())
h += i.next().hashCode();
return h;
}
6.獲取三個主要的檢視
獲取所有的鍵:
public Set<K> keySet() {
//如果成員變數 keySet 為 null,建立個空的 AbstractSet
if (keySet == null) {
keySet = new AbstractSet<K>() {
public Iterator<K> iterator() {
return new Iterator<K>() {
private Iterator<Entry<K,V>> i = entrySet().iterator();
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;
}
獲取所有的值:
public Collection<V> values() {
if (values == null) {
//沒有就建立個空的 AbstractCollection 返回
values = new AbstractCollection<V>() {
public Iterator<V> iterator() {
return new Iterator<V>() {
private Iterator<Entry<K,V>> i = entrySet().iterator();
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;
}
獲取所有鍵值對,需要子類實現:
public abstract Set<Entry<K,V>> entrySet();
AbstractMap 中的內部類
正如 Map 介面 中有內部類 Map.Entry 一樣, AbstractMap 也有兩個內部類:
- SimpleImmutableEntry, 表示一個不可變的鍵值對
- SimpleEntry, 表示可變的鍵值對
SimpleImmutableEntry,不可變的鍵值對,實現了 Map.Entry < K,V> 介面:
public static class SimpleImmutableEntry<K,V>
implements Entry<K,V>, java.io.Serializable
{
private static final long serialVersionUID = 7138329143949025153L;
//key-value
private final K key;
private final V value;
//建構函式,傳入 key 和 value
public SimpleImmutableEntry(K key, V value) {
this.key = key;
this.value = value;
}
//建構函式2,傳入一個 Entry,賦值給本地的 key 和 value
public SimpleImmutableEntry(Entry<? extends K, ? extends V> entry) {
this.key = entry.getKey();
this.value = entry.getValue();
}
//返回 鍵
public K getKey() {
return key;
}
//返回 值
public V getValue() {
return value;
}
//修改值,不可修改的 Entry 預設不支援這個操作
public V setValue(V value) {
throw new UnsupportedOperationException();
}
//比較指定 Entry 和本地是否相等
//要求順序,key-value 必須全相等
//只要是 Map 的實現類即可,不同實現也可以相等
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());
}
//返回一個 String
public String toString() {
return key + "=" + value;
}
}
SimpleEntry, 可變的鍵值對:
public static class SimpleEntry<K,V>
implements Entry<K,V>, java.io.Serializable
{
private static final long serialVersionUID = -8499721149061103585L;
private final K key;
private V value;
public SimpleEntry(K key, V value) {
this.key = key;
this.value = value;
}
public SimpleEntry(Entry<? extends K, ? extends V> entry) {
this.key = entry.getKey();
this.value = entry.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;
}
}
SimpleEntry 與 SimpleImmutableEntry 唯一的區別就是支援 setValue() 操作。
總結
常用的幾種 Map, 比如 HashMap, TreeMap, LinkedHashMap 都繼承自它。
Thanks
還有肉肉做的紫菜包飯,讓我今天能量十足!
相關推薦
Java 集合深入理解(15):AbstractMap
今天來了解下 AbstractMap。 什麼是 AbstractMap AbstractMap 是 Map 介面的的實現類之一,也是 HashMap, TreeMap, ConcurrentHashMap 等類的父類。 Abst
Java 集合深入理解(4):List 介面
在 Java 集合深入理解:Collection 中我們熟悉了 Java 集合框架的基本概念和優點,也瞭解了根介面之一的 Collection,這篇文章來加深 Collection 的子介面之一 List 的熟悉。 List 介面 一個 List 是一個元素有
Java 集合深入理解(3):Collection
今天心情有點粉,來學學 Collection 吧! 什麼是集合? 集合,或者叫容器,是一個包含多個元素的物件; 集合可以對資料進行儲存,檢索,操作; 它們可以把許多個體組織成一個整體: 比如一副撲克牌(許多牌組成的集合); 比如一個電話本
Java 集合深入理解(9):Queue 佇列
今天心情不太好,來學一下 List 吧! 什麼是佇列 佇列是資料結構中比較重要的一種型別,它支援 FIFO,尾部新增、頭部刪除(先進佇列的元素先出佇列),跟我們生活中的排隊類似。 佇列有兩種: 單佇列 迴圈佇列 單佇
Java 集合深入理解(14):Map 概述
終於把 List 常用的幾種容器介紹完了,接下來開始 Map 的相關介紹。 什麼是 Map Java 中的 Map 介面 是和 Collection 介面 同一等級的集合根介面,它 表示一個鍵值對 (key-value) 的對映。類似數學
Java 集合深入理解(17):HashMap 在 JDK 1.8 後新增的紅黑樹結構
上篇文章我們介紹了 HashMap 的主要特點和關鍵方法原始碼解讀,這篇文章我們介紹 HashMap 在 JDK1.8 新增樹形化相關的內容。 讀完本文你將瞭解到: 傳統 HashMap 的缺點 JDK 1.8 以前 HashMap 的實
Java 集合深入理解(7):ArrayList
今天心情有點美麗,學學 ArrayList 放鬆下吧! 什麼是 ArrayList ArrayList 是 Java 集合框架中 List介面 的一個實現類。 可以說 ArrayList 是我們使用最多的 List 集合,它
Java 集合深入理解(6):AbstractList
今天心情比天藍,來學學 AbstractList 吧! 什麼是 AbstractList AbstractList 繼承自 AbstractCollection 抽象類,實現了 List 介面 ,是 ArrayList 和 Abstr
Java 集合原始碼解析(1):Iterator
Java 提供的 集合類都在 Java.utils 包下,其中包含了很多 List, Set, Map, Queue… 它們的關係如下面這張類圖所示: 可以看到,Java 集合主要分為兩類:Collection 和 Map. 而 Collection 又繼承了 Iter
java基礎學習總結(九):深入理解Java泛型
一、什麼是泛型 “泛型” 意味著編寫的程式碼可以被不同型別的物件所重用。泛型的提出是為了編寫重用性更好的程式碼。泛型的本質是引數化型別,也就是說所操作的資料型別被指定為一個引數。 比如常見的集合類 LinkedList: publi
深入理解設計模式(15):訪問者模式
一、什麼是訪問者模式 定義:表示一個作用於其物件結構中的各元素的操作,它使你可以在不改變各元素類的前提下定義作用於這些元素的新操作。 可以對定義這麼理解:有這麼一個操作,它是作用於一些元素之上的,而這些元素屬於某一個物件結構。同時這個操作是在不改變各元素類的前提下,在這個前提下定義新操作是訪問者模式精髓中
深入理解JavaScript系列(15):函式(Functions)
詳情請檢視:https://www.cnblogs.com/TomXu/archive/2012/01/30/2326372.html 本章節我們要著重介紹的是一個非常常見的ECMAScript物件——函式(function),我們將詳細講解一下各種型別的函式
深入理解java虛擬機器系列(一):java記憶體區域與記憶體溢位異常
文章主要是閱讀《深入理解java虛擬機器:JVM高階特性與最佳實踐》第二章:Java記憶體區域與記憶體溢位異常 的一些筆記以及概括。 好了開始。如果有什麼錯誤或者遺漏,歡迎指出。 一、概述 先上一張圖 這張圖主要列出了Java虛擬機器管理的記憶體的幾個區域。 常有人
深入學習理解(4):java :ExecutorService中submit和execute的區別
在Java5之後,併發執行緒這塊發生了根本的變化,最重要的莫過於新的啟動、排程、管理執行緒的一大堆API了。在Java5以後,通過Executor來啟動執行緒比用Thread的start()更好。在新特徵中,可以很容易控制執行緒的啟動、執行和關閉過程,還可以很容
Java類集框架(四):集合輸出
集合輸出的4種形式:Iterator輸出、ListIterator輸出、foreach(加強型for迴圈)輸出、Enumeration輸出。 迭代輸出:Iterator Iterator(迭代器)是集合輸出操作的一個介面,Collection介面中提供了直接為Iterator介面例項
深入學習Gremlin(15):分支操作
第15期 Gremlin Steps: coalesce()、optional()、union() 本系列文章的Gremlin示例均在HugeGraph圖資料庫上執行,環境搭建可參考準備Gremlin執
JAVA設計模式(15):迭代器模式
迭代器模式是Java和.Net程式設計環境中非常常用的設計模式。此模式用於以順序方式訪問集合物件的元素,而不需要知道其底層表示。迭代器模式屬於行為模式類別。 實現例項 在這個例項中,將建立一個Iterator介面,它陳述了一個導航方法和一個Container介面,以及返回迭代器。 實現Con
理解 neutron(15):Neutron Linux Bridge + VLAN/VXLAN 虛擬網路
學習 Neutron 系列文章: 雖然大部分的OpenStack 部署環境中,都會使用 Open vSwitch 來作為虛擬交換機來實現二層網路功能,但是Neutron 仍然支援使用 Linux bridge 作為虛擬交換機來實現二層網路。
Java執行緒總結(八):併發包------讀寫鎖ReadWriteLock的簡單例子詳細理解
初次接觸ReadWriteLock類時也在網上查了很多資料,很容易瞭解到ReadWriteLock是讀寫鎖,並且讀寫鎖的機制有以下三個特點: 讀鎖---讀鎖 (不互斥) 讀鎖---寫鎖 (互斥) 寫鎖---寫鎖 (互斥)什麼意思呢?網上很多資料,
深入Java虛擬機器筆記(一):Java記憶體區域與記憶體溢位異常
1、程式計數器為很小的記憶體空間,為當前執行緒執行的位元組碼的行號指示器,通過改變計數器的值來選取下一條需要執行的位元組碼指令,迴圈、分支等基礎功能都是需要計數器來完成的 2、Java虛擬機器棧為Java方法執行的記憶體模型,每個方法被執行時都會同時建立棧