Java中的Map【一】Map接口

一 JDK中的Map繼承實現關係 

       不經意間看了Java中LinkedHashMap和LinkedHashSet的源碼實現,覺得一些地方還是挺有意思的。之前閱讀過一些,但沒有進行系統性地總結,打算嘗試一下Map源碼的系統性整理學習。因爲Java中的Set底層基本上是藉助對應的Map實現的,故Set打算放在Map之後學習。所使用的jdk版本爲1.8版本,先看一下JDK中Map的UML類圖:

       這張圖囊括了JDK中絕大部分的Map(沒有將java.util.Collections中的一些private static map結構、java.util.EnumMap等列入),後面逐一分析,希望能有所收穫。

二 源碼分析學習

2.1 相關接口

2.1.1 Map接口

     Map<K,V>是最基本的接口,它表示將鍵映射到值的對象。一個映射不能包含重複的鍵;每個鍵最多隻能映射到一個值。此接口被設計用來取代 java.util.Dictionary 類,後者完全是一個抽象類,而不是一個接口。因爲Map涉及到Key和Value兩個對象,所以它不像List<E>或者Set<E>接口都實現了Collection<E>接口,Map<K,V>沒有繼承任何接口。有些Map的實現可以保證順序,如TreeMap;有些Map的實現不保證順序,如HashMap;還有特殊的Map實現比如LinkedHashMap具有可預知的迭代順序,該迭代順序可以是插入順序或者是訪問順序。

       下面列一些需要注意的地方:

2.1.1.1 containsKey(Object key)方法

      Map中需要注意的containsKey(Object key)方法,Map是否已經包含有某個對象key的判斷條件:當且僅當此映射包含針對滿足 (key==null ? k==null : key.equals(k)) 的鍵 k 的映射關係時,返回 true。(最多隻能有一個這樣的映射關係)。可能日常開發中常用字符串String作爲key,而String已經幫我們實現了Object類中的equals()方法和hashCode()方法,所以當我們使用自定義的對象作爲key時,一定要考慮這兩個方法的實現。containsKey(Object key)定義如下:

    /**
     * Returns <tt>true</tt> if this map contains a mapping for the specified
     * key.  More formally, returns <tt>true</tt> if and only if
     * this map contains a mapping for a key <tt>k</tt> such that
     * <tt>(key==null ? k==null : key.equals(k))</tt>.  (There can be
     * at most one such mapping.)
     *
     * @param key key whose presence in this map is to be tested
     * @return <tt>true</tt> if this map contains a mapping for the specified
     *         key
     * @throws ClassCastException if the key is of an inappropriate type for
     *         this map
     * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
     * @throws NullPointerException if the specified key is null and this map
     *         does not permit null keys
     * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
     */
    boolean containsKey(Object key);

2.1.1.2 entrySet()方法和內部接口Entry<K,V>    

        Map接口中還定義了一個內部接口Entry<K,V>,用來表示Map中的映射項;另外有一個方法Set<Map.Entry<K, V>> entrySet();兩者結合可以用來遍歷Map中的元素。比如下面寫法:

for (Map.Entry<String, Object> entry : map.entrySet()) {
            System.out.println("key:value = " + entry.getKey() + ":" + entry.getValue());
        }

2.1.1.3  V put(K key, V value)方法

       將映射關係放入Map結構中,需要注意的是如果此映射以前包含一個該鍵的映射關係,則用指定值替換舊值(當且僅當m.containsKey(k)返回 true 時,才能說映射 m 包含鍵 k 的映射關係)。

       返回值:返回之前與 key 關聯的舊值,如果沒有針對 key 的映射關係,則返回 null。(如果該實現支持 null 值,則返回 null 也可能表示此映射以前將 null 與 key 關聯)。

2.1.1.4  Set<K> keySet() 和 Collection<V> values()

        Set<K> keySet()方法返回Map中所有映射關係中key的集合,因爲key是不能重複的,所以返回的是Set結構;Collection<V> value()方法返回Map中所有映射關係值value的集合,value的值是可以重複的,所以返回的是Collection結構,重複的value值也會重複返回,不會去重。

       通過keySet()方法和get(Object key)方法也能遍歷Map:

for (String key : map.keySet()) {
            System.out.println("map.get(" + key + ") = " + map.get(key));
        }

2.1.1.5  equals(Object o) 和 hashCode()

        equals(Object o)方法用來比較兩個Map是否相等,如果給定的對象也是一個映射,並且這兩個映射表示相同的映射關係,則返回 true。更確切地講,如果 m1.entrySet().equals(m2.entrySet()),則兩個映射 m1 和 m2 表示相同的映射關係。

       hashCode()方法返回此映射的哈希碼值。映射的哈希碼定義爲此映射 entrySet() 中每個項的哈希碼之和。這確保 m1.equals(m2) 對於任意兩個映射 m1 和 m2 而言,都意味着 m1.hashCode()==m2.hashCode(),正如Object.hashCode()中的要求。

       equals(Object o)和hashCode()的具體實現,可以參考java.util.AbstractMap中的實現,後面也會介紹。

2.1.1.6  JDK1.8版本新增加的特性

1、default V getOrDefault(Object key, V defaultValue)

        一個default方法,返回Map中key對應的value值,如果沒有這個key,返回傳入的默認值defaultValue:

    default V getOrDefault(Object key, V defaultValue) {
        V v;
        return (((v = get(key)) != null) || containsKey(key))
            ? v
            : defaultValue;
    }

使用示例:

 public static void main(String[] args) {
       Map map = new HashMap();
       map.put("1", 12);
       map.put("11", 12);
       map.put("12", 13);
       System.out.println(map.getOrDefault("11",120));
       System.out.println(map.getOrDefault("13",120));
    }

輸出結果:

12
120

2、default void forEach(BiConsumer<? super K, ? super V> action) 

      該forEach方法採用函數式編程,傳入一個函數式接口BiConsumer,用來迭代遍歷Map,如下:

 map.forEach((k, v) -> System.out.println("key:value = " + k + ":" + v));

3、default void repalceAll(BiFunction<? super K, ? super V> action) 

default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
        Objects.requireNonNull(function);
        for (Map.Entry<K, V> entry : entrySet()) {
            K k;
            V v;
            try {
                k = entry.getKey();
                v = entry.getValue();
            } catch(IllegalStateException ise) {
                // this usually means the entry is no longer in the map.
                throw new ConcurrentModificationException(ise);
            }

            // ise thrown from function is not a cme.
            v = function.apply(k, v);

            try {
                entry.setValue(v);
            } catch(IllegalStateException ise) {
                // this usually means the entry is no longer in the map.
                throw new ConcurrentModificationException(ise);
            }
        }
    }

      用給定的函數action對Map中的每個映射進行處理,並用每個映射執行action返回的結果替換映射對應的value值。舉個例子:

public static void main(String[] args) {
        Map<String,Integer> map = new HashMap();
        map.put("1", 1);
        map.put("2", 2);
        map.put("3", 3);
        System.out.println(map);
        //對map中的每個映射value值都加1
        map.replaceAll((k,v) ->  v +=1);
        System.out.println(map);
    }

輸出結果:

{1=1, 2=2, 3=3}
{1=2, 2=3, 3=4}

4、default V putIfAbsent(K key, V value)

default V putIfAbsent(K key, V value) {
        V v = get(key);
        if (v == null) {
            v = put(key, value);
        }

        return v;
    }

     如果入參中的key在Map中沒有映射的value值或者映射的value值爲null,把入參中的value值與之關聯,返回null。反之,返回Map中該key映射的value值。

使用示例:

public static void main(String[] args) {
        Map<String, Integer> map = new HashMap();
        map.put("1", 1);
        map.put("2", null);
        System.out.println("初始map:" + map);
        System.out.println("map.putIfAbsent(\"1\", 11)返回值:" + map.putIfAbsent("1", 11));
        System.out.println("map.putIfAbsent(\"2\", 2)返回值:" + map.putIfAbsent("2", 2));
        System.out.println("map.putIfAbsent(\"3\", 3)返回值:" + map.putIfAbsent("3", 3));
        System.out.println("處理後map:" + map);
    }

輸出結果:

初始map:{1=1, 2=null}
map.putIfAbsent("1", 11)返回值:1
map.putIfAbsent("2", 2)返回值:null
map.putIfAbsent("3", 3)返回值:null
處理後map:{1=1, 2=2, 3=3}

       注:還有一些其它的default函數,這裏不再一一列舉了,都是JDK1.8中根據default特性新加在Map接口中的,大家可以自己瞭解下,實際開發使用會便利一些。

       以上,Map接口暫時就寫這麼多~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章