Java 集合體系之 AbstractMap 源碼分析

前言

AbstractMap 是一個實現了 Map 接口的抽象類,該類提供了 Map 接口的骨架實現,以最大限度的減少實現此接口所帶來的工作量。

要實現一個不可修改的 Map,我們只需要擴展此類,並實現 entrySet() 方法(AbstractMap 唯一的抽象方法)即可,該方法返回存有 Map 映射關係的 Set 集合。通常,返回的 Set 又將在 AbstractSet 上實現。此 Set 不應該支持 add() 或 remove() 方法,其迭代器也不應該支持 remove() 方法。

要實現一個可修改的 Map,我們必須另外重寫此類的 put 方法(否則將拋出 UnsupportedOperationException 異常),並且由 entrySet().iterator() 返回的迭代器也必須另外實現其 remove() 方法。

按照 Map 接口規範中的建議,我們通常應該提供一個 void(無參數)構造方法和 map 構造方法。

源碼分析

構造方法

唯一的無參構造函數,用於子類構造函數的調用,通常是隱式的:

protected AbstractMap() {
}

成員變量

其成員變量有如下兩個:

transient Set<K>        keySet;
transient Collection<V> values;

都是由 transient 關鍵字所修飾,表示在序列化時不保存。
並且都只是聲明瞭,並沒有被賦值。賦值將會在下面的 keySet() 與 values() 方法中操作。
- keySet:保存所有 key 的 Set 集合。
- values:保存所有 value 的 Collection 集合。

抽象方法

抽象方法就只有一個,返回保存 Map 中所有映射條目的 Set 集合:

public abstract Set<Entry<K,V>> entrySet();

實例方法

查詢操作

返回集合的長度:

public int size() {
    return entrySet().size();
}

返回集合是否爲空,爲空時返回 true,否則返回 false:

public boolean isEmpty() {
    // 當集合中的元素個數爲 0 時返回 true
    return size() == 0;
}

返回集合中是否存在指定的 value(支持 null),存在則返回 true,否則返回 false:

public boolean containsValue(Object value) {
    // 獲取迭代器
    Iterator<Entry<K,V>> i = entrySet().iterator();
    // 循環遍歷取出 value 與 指定的 value 進行比對,比對成功返回 true
    if (value==null) {
        while (i.hasNext()) {
            Entry<K,V> e = i.next();
            if (e.getValue()==null)
                return true;
        }
    } else {
        while (i.hasNext()) {
            Entry<K,V> e = i.next();
            if (value.equals(e.getValue()))
                return true;
        }
    }
    // 沒有找到,返回 false
    return false;
}

返回集合中是否存在指定的 key(支持 null),存在則返回 true,否則返回 false:

public boolean containsKey(Object key) {
    // 獲取迭代器
    Iterator<Map.Entry<K,V>> i = entrySet().iterator();
    // 循環遍歷取出 key 與 指定的 key 進行比對,比對成功返回 true
    if (key==null) {
        while (i.hasNext()) {
            Entry<K,V> e = i.next();
            if (e.getKey()==null)
                return true;
        }
    } else {
        while (i.hasNext()) {
            Entry<K,V> e = i.next();
            if (key.equals(e.getKey()))
                return true;
        }
    }
    // 沒有找到,返回 false
    return false;
}

獲取指定 key 所映射的 value,沒有找到,則返回 null:

public V get(Object key) {
    // 獲取迭代器
    Iterator<Entry<K,V>> i = entrySet().iterator();
    // 循環遍歷,當找到對應的 key 時取出與之相對應的 value
    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();
        }
    }
    // 沒有找到,返回 null
    return null;
}

修改操作

將指定的 key 與 value 相關聯(可選操作,意味着實現類可以實現該方法,也可以不實現,開頭已經說過了,當想實現一個可修改的集合時可以重寫修改操作的相關方法):

public V put(K key, V value) {
    // AbstractMap 的實現是直接拋出一個異常
    throw new UnsupportedOperationException();
}

刪除指定 key 的映射條目,刪除成功則返回指定 key 所映射的 value,刪除失敗,返回 null:

public V remove(Object key) {
    // 獲取迭代器
    Iterator<Entry<K,V>> i = entrySet().iterator();
    // 定義一個映射條目變量,初始化爲 null
    Entry<K,V> correctEntry = null;
    // 當 correntEntry 沒有被賦值並集合中還有映射條目時
    // 就一直循環遍歷集合找出與指定 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;
    // 當找到了指定 key 的映射條目,並且不爲 null 時
    if (correctEntry !=null) {
        // 獲取 key 所映射的 value,方法結束時返回該值
        oldValue = correctEntry.getValue();
        // 調用迭代器的刪除方法,刪除該映射條目
        // 注意迭代器的 remove() 方法實現也是拋出異常
        // 所以在實現一個可修改的集合時別忘了重寫迭代器的 remove() 方法
        i.remove();
    }
    // 存在指定 key 的映射條目時返回其所映射的 value,否則返回 null
    return oldValue;
}

批量操作

將指定參數 Map 集合中的映射數據存儲到當前 Map 中:

public void putAll(Map<? extends K, ? extends V> m) {
    // 遍歷指定的 Map 集合的映射條目
    for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
        // 將映射中的 key、value 取出並存儲到當前 Map中
        // 注意這裏調用了 put() 方法,所以如果子類沒有重寫 put() 方法,那麼該方法將會拋出異常
        put(e.getKey(), e.getValue());
}

清空當前 Map:

public void clear() {
    entrySet().clear();
}

視圖

返回存儲所有 key 的 Set 視圖集合:

public Set<K> keySet() {
    Set<K> ks = keySet;
    // 當 key 的視圖集合是空,則創建 AbstractSet 對象然後賦值
    if (ks == null) {
        ks = new AbstractSet<K>() {
            // AbstractSet 是一個抽象類,於是需要實現其抽象方法
            public Iterator<K> iterator() {
                // 返回的迭代器方法都是調用的 enterySet() 的迭代器實現
                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();
                    }
                };
            }
            // 其他方法也都是調用的當前抽象類 AbstractMap 的方法實現
            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);
            }
        };
        keySet = ks;
    }
    // 返回 key 的 Set 視圖集合
    return ks;
}

返回存儲所有 value 的 Collection 視圖集合:

public Collection<V> values() {
    // 該方法的實現同 keySet() 方法
    // 只不過由 Set 的 AbstractSet 對象實例,變爲了 Collection 的 AbstractCollection 對象實例。
    Collection<V> vals = values;
    if (vals == null) {
        vals = 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);
            }
        };
        values = vals;
    }
    return vals;
}

比較和散列

比較指定的對象是否與當前 Map相等:

public boolean equals(Object o) {
    // 比較地址值相等,直接返回 true
    if (o == this)
        return true;
    // 比較當指定對象不是 Map 或其實現類的實例,返回 false
    if (!(o instanceof Map))
        return false;
    // 強轉爲 map
    Map<?,?> m = (Map<?,?>) o;
    // 長度和當前 Map不一致,返回 false
    if (m.size() != size())
        return false;

    try {
        // 獲取當前 Map的迭代器
        Iterator<Entry<K,V>> i = entrySet().iterator();
        // 遍歷映射條目
        while (i.hasNext()) {
            // 獲取 value 與指定 map 對象的 value 進行比較,不相等則返回 false 
            Entry<K,V> e = i.next();
            K key = e.getKey();
            V value = e.getValue();
            if (value == null) {
                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;
    }
    // 當以上所有校驗比對都通過了,那麼返回 true
    return true;
}

返回當前 Map 的 hashCode:

public int hashCode() {
    int h = 0;
    Iterator<Entry<K,V>> i = entrySet().iterator();
    while (i.hasNext())
        // AbstractMap 的 hashCode 計算
        // 是其每個映射條目的 hashCode 的總和
        h += i.next().hashCode();
    return h;
}

其他方法

返回當前 Map 的字符串表示形式:

public String toString() {
    // 獲取迭代器
    Iterator<Entry<K,V>> i = entrySet().iterator();
    // 沒有數據時,直接返回:{}
    if (! i.hasNext())
        return "{}";
    // 創建 StringBuilder 對象,用於拼接字符串
    StringBuilder sb = new StringBuilder();
    // 循環開始前,拼接開始括號: {
    sb.append('{');
    for (;;) {
        // 獲取映射條目的 key、value
        Entry<K,V> e = i.next();
        K key = e.getKey();
        V value = e.getValue();
        // 當 key 或者 value 是當前 Map 時,拼接 (this Map),否則拼接其 key、value
        sb.append(key   == this ? "(this Map)" : key);
        sb.append('=');
        sb.append(value == this ? "(this Map)" : value);
        // 沒有元素了,拼接結束括號:},並返回
        if (! i.hasNext())
            return sb.append('}').toString();
        sb.append(',').append(' ');
    }
}

返回此 AbstractMap 實例的淺拷貝(鍵和值本身不被克隆):

protected Object clone() throws CloneNotSupportedException {
    // 調用 Native 方法
    AbstractMap<?,?> result = (AbstractMap<?,?>)super.clone();
    // 將存儲 key、value 的集合置爲 null
    result.keySet = null;
    result.values = null;
    return result;
}

私有靜態方法,給 SimpleEntry 和 SimpleImmutableEntry 內部類用的。
用於檢查給定的兩個對象是否相等(包括 null):

private static boolean eq(Object o1, Object o2) {
    return o1 == null ? o2 == null : o1.equals(o2);
}

SimpleEntry

Map.Entry 接口的實現類,從 1.6 開始提供。
保存 key 和 value 映射的條目,在該內部類中其 value 可以用 setValue() 方法進行更改:

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;

    /**
     * 創建指定 key、value 的映射條目。
     */
    public SimpleEntry(K key, V value) {
        this.key   = key;
        this.value = value;
    }

    /**
     * 從指定映射條目中獲取的 key、value 來創建映射條目。
     */
    public SimpleEntry(Entry<? extends K, ? extends V> entry) {
        this.key   = entry.getKey();
        this.value = entry.getValue();
    }

    /**
     * 獲取 key
     */
    public K getKey() {
        return key;
    }

    /**
     * 獲取 value
     */
    public V getValue() {
        return value;
    }

    /**
     * 設置新的 value 值,並返回舊的 value 值。
     */
    public V setValue(V value) {
        V oldValue = this.value;
        this.value = value;
        return oldValue;
    }

    /**
     * 將指定的對象與當前映射條目進行比對,比對成功返回 true,否則返回 false。
     */
    public boolean equals(Object o) {
        // 如果指定對象不是 Map.Entry 接口的實現類則直接返回 false
        if (!(o instanceof Map.Entry))
            return false;
        // 強轉爲 Map.Entry
        Map.Entry<?,?> e = (Map.Entry<?,?>)o;
        // 使用 eq() 靜態方法對 key 與 value 進行比對
        return eq(key, e.getKey()) && eq(value, e.getValue());
    }

    /**
     * 返回映射條目的 hasCode。
     */
    public int hashCode() {
        // hasCode 的計算是根據 key 與 value 的 hasCode 來的
        return (key   == null ? 0 :   key.hashCode()) ^
               (value == null ? 0 : value.hashCode());
    }

    /**
     * 返回映射條目的字符串表現形式。
     */
    public String toString() {
        return key + "=" + value;
    }

}

SimpleImmutableEntry

顧名思義,不可變的 SimpleEntry。該內部類同樣是 Map.Entry 接口的實現類,從 1.6 開始提供。
與 SimpleEntry 唯一的不同就是,其 setValue() 方法是拋出了異常:

public static class SimpleImmutableEntry<K,V>
    implements Entry<K,V>, java.io.Serializable
{
    private static final long serialVersionUID = 7138329143949025153L;

    private final K key;
    private final V value;

    public SimpleImmutableEntry(K key, V value) {
        this.key   = key;
        this.value = 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;
    } 
    public V setValue(V value) {
        // look here
        throw new UnsupportedOperationException();
    }

    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;
    }
}

到這,AbstractMap 抽象類的源碼就看完了。最後再來總結一下。

總結

AbstractMap 作爲實現 Map 接口的抽象類,其作用及目的就是簡化實現 Map 接口所帶來的工作量。像常用的 TreeMap、HashMap 等都是繼承自它。我們在自己設計框架的時候,其實也是可以採用這種設計思想的。

發佈了55 篇原創文章 · 獲贊 117 · 訪問量 30萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章