Map接口和AbstractMap抽象類詳解

爲了更好的理解JDK的哈希表實現,首先研究Map接口和AbstractMap抽象類,打好基礎很重要~

Map接口

先來研究下Map接口,附上了註釋的Map接口定義如下:

/**
  Map接口是鍵、值對接口,Map中的鍵是唯一的
 */
public interface Map<K,V> {
    /**
     Map的鍵值對數量,如果鍵值對數量大於Integer.MAX_VALUE,該方法將會返回Integer.Max_VALUE
    */
    int size();
    /**
     該Map是否存在鍵值對
    */
    boolean isEmpty();
    /**
     該Map是否包含鍵key,當該Map存在鍵k,該方法返回值爲:
     key == null ? k == null : key.equals(k);
    */
    boolean containsKey(Object key);
    /**
     該Map是否存在鍵值對的值爲value,當該Map存在值爲v,該方法返回值爲:
     value == null ? v == null : value.equals(v);
    */
    boolean containsValue(Object value);
    /**
     返回該Map中鍵爲key的值,若不存在,返回null
    */
    V get(Object key);
    /**
     將鍵值對k、value加入到Map中,若已經存在鍵key,則將新的value值關聯到鍵key
    */
    V put(K key, V value);
    /**
     刪除鍵key對應的鍵值對,返回刪除之前key關聯的value,若不存在key,返回null
    */
    V remove(Object key);
    /**
     將m的鍵值對加入到本Map中
    */
    void putAll(Map<? extends K, ? extends V> m);
    /**
     刪除本Map中所有鍵值對
    */
    void clear();
    /**
     返回鍵的集合
    */
    Set<K> keySet();
    /**
     返回值的集合
    */
    Collection<V> values();
    /**
     鍵值對的集合
    */
    Set<Map.Entry<K, V>> entrySet();
    /**
     鍵值對
    */
    interface Entry<K,V> {

        K getKey();

        V getValue();

        V setValue(V value);

        boolean equals(Object o);

        int hashCode();
    }

    boolean equals(Object o);
    /**
     該Map的哈希碼
    */
    int hashCode();

}

AbstractMap抽象類

AbstractMap抽象類實現了Map接口,它提供了一個實現Map接口的框架,簡化了子類實現Map接口所需要做的很多通用的功能,子類可以繼承該抽象類來決定具體怎麼實現鍵值對的映射,該抽象類定義如下(只列出方法的聲明,具體實現下面還要詳細介紹):

public abstract class AbstractMap<K,V> implements Map<K,V> {

    //鍵的集合
    transient volatile Set<K>        keySet = null;
    //值的集合
    transient volatile Collection<V> values = null;

    protected AbstractMap() {
    }

    public int size() {
    }

    public boolean isEmpty() {
    }

    public boolean containsValue(Object value) {
    }

    public boolean containsKey(Object key) {
    }

    public V get(Object key) {
    }

    public V put(K key, V value) {
    }

    public V remove(Object key) {
    }

    public void putAll(Map<? extends K, ? extends V> m) {
    }

    public void clear() {
    }

    public Set<K> keySet() {
    }

    public Collection<V> values() {
    }

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

    public boolean equals(Object o) {
    }

    public int hashCode() {
    }

    public String toString() {
    }

    protected Object clone() throws CloneNotSupportedException {
    }

    private static boolean eq(Object o1, Object o2) {
    }

    public static class SimpleEntry<K,V>
        implements Entry<K,V>, java.io.Serializable
    {
    }

    public static class SimpleImmutableEntry<K,V>
        implements Entry<K,V>, java.io.Serializable
    {
    }

}

觀察AbstractMap的結構,除了entrySet()這個方法定義爲抽象的,Map接口中的其它方法都已經給出實現。

另外,該抽象類包含了兩個靜態內部類:SimpleEntry和SimpleImmutableEntry。

該抽象類有兩個字段:keySet和values,分別存儲鍵集合和值集合。這兩個字段爲何是transient修飾的?這是因爲子類會決定如何去序列化,例如HashMap就實現了writeObject方法和readObject方法。

下面我們對每一個方法進行解釋:

size方法

size方法的源碼實現如下:

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

該方法調用了entrySet方法,entrySet方法返回的是鍵值對的集合,該方法也是AbstractMap唯一沒有實現的方法(定義爲抽象的)。

isEmpty方法

該方法的實現非常簡單,通過調用size方法,確定鍵值對的數量是否爲0即可,源碼實現如下:

public boolean isEmpty() {
    return size() == 0;
}

containsValue方法

該方法的源碼實現如下:

public boolean containsValue(Object value) {
    Iterator<Entry<K,V>> i = entrySet().iterator();
    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;
      }
    }
    return false;
}

該方法實現的基本思想是首先取鍵值對集合的迭代器,然後根據value值是否爲null分別處理。

  • 如果value爲空,迭代器每次迭代的時候只要判斷當前鍵值對的值是否爲null即可,如果爲null表示該Map包含值爲null的鍵值對,返回true,否則繼續遍歷剩下的鍵值對。
  • 如果value非空,迭代器每次迭代的時候通過調用value的equals方法判斷是否和當前鍵值對的值相等,如果相等表示該Map包含值爲value的鍵值對,返回true,否則繼續遍歷剩下的鍵值對。

containsKey方法

該方法的源碼實現如下:

public boolean containsKey(Object key) {
    Iterator<Map.Entry<K,V>> i = entrySet().iterator();
    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;
      }
    }
    return false;
}

該方法的實現與containsValue的實現基本相同,不再說明。

由containsValue和containsKey兩個方法可知,AbstractMap的實現是支持鍵和值爲null的場景,這點需要注意。

get方法

該方法的源碼實現如下:

public V get(Object 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;
}

該方法的實現和containsKey和containsValue方法的實現基本相同,不再說明。

put方法

AbstractMap對該方法的實現僅是拋出異常,子類需要去實現。因爲具體的實現方法是由子類來決定的,因此這裏拋出異常是合理的。源碼如下:

public V put(K key, V value) {
    throw new UnsupportedOperationException();
}

remove方法

remove方法根據鍵key查找鍵值對,然後調用迭代器的remove方法刪除該項。

public V remove(Object key) {
    //鍵值對的迭代器
    Iterator<Entry<K,V>> i = entrySet().iterator();
    //correctEntry代表最終找到的那個鍵值對,若沒有找到則爲null
    Entry<K,V> correctEntry = null;
    if (key==null) {
      //key爲null的情況
      while (correctEntry==null && i.hasNext()) {
        Entry<K,V> e = i.next();
        if (e.getKey()==null)
          //找到了鍵爲key的鍵值對,while循環將結束
          correctEntry = e;
      }
    } else {
      //key不爲null的情況
      while (correctEntry==null && i.hasNext()) {
        Entry<K,V> e = i.next();
        if (key.equals(e.getKey()))
          correctEntry = e;
      }
    }
    //oldValue表示的是該方法的返回值,即將要刪除的鍵值對的值
    V oldValue = null;
    if (correctEntry !=null) {
      oldValue = correctEntry.getValue();
      //調用迭代器的remove方法刪除
      i.remove();
    }
    return oldValue;
}

putAll方法

putAll方法通過調用put方法將m中的所有鍵值對加入到本map

public void putAll(Map<? extends K, ? extends V> m) {
    for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
      put(e.getKey(), e.getValue());
}

clear方法

clear方法首先通過entrySet方法得到鍵值對的set集合,然後通過set集合的clear方法清空本map

public void clear() {
    //其實是通過調用鍵值對set的clear方法清空的
    entrySet().clear();
}

keySet方法

該方法獲得鍵的set,源碼如下:

public Set<K> keySet() {
    if (keySet == null) {
      //若成員變量爲空將會創建一個
      keySet = new AbstractSet<K>() {
        //實現AbstractSet的迭代器方法
        public Iterator<K> iterator() {
          //創建一個迭代器
          return new Iterator<K>() {
            //通過外部類的entrySet方法獲得鍵值對的迭代器
            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();
            }
          };
        }
        //實現AbstractSet的size方法
        public int size() {
          //其實是調用外部類的size方法
          return AbstractMap.this.size();
        }
        //實現AbstractSet的isEmpty方法
        public boolean isEmpty() {
          //調用外部類的isEmpty方法
          return AbstractMap.this.isEmpty();
        }
        //實現AbstractSet的clear方法
        public void clear() {
          //調用外部類的clear方法
          AbstractMap.this.clear();
        }
        //實現AbstractSet的conains方法
        public boolean contains(Object k) {
          //通過外部類的containsKey實現
          return AbstractMap.this.containsKey(k);
        }
      };
    }
    return keySet;
}

該方法首先判斷成員變量keySet是否爲空,若不爲空,直接返回,否則將初始化一個keySet。

可以看到,最終其實是根據鍵值對的迭代器實現的。

values方法

該方法返回值的集合,實現方式和keySet類似,只不過這裏返回的是Collection,而keySet返回的是Set,源碼如下:

 public Collection<V> values() {
     if (values == null) {
       //若成員變量values爲空,則根據AbstractCollection創建一個
       values = new AbstractCollection<V>() {
         //實現AbstractCollection的迭代器方法
         public Iterator<V> iterator() {
           //新建一個迭代器
           return new Iterator<V>() {
             //通過外部類的entrySet方法獲得鍵值對的迭代器
             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();
             }
           };
         }
        //實現AbstractCollection的size方法
         public int size() {
           //通過調用外部類的size方法實現
           return AbstractMap.this.size();
         }
        //實現AbstractCollection的isEmpty方法
         public boolean isEmpty() {
           //通過調用外部類的isEmpty方法實現
           return AbstractMap.this.isEmpty();
         }
        //實現AbstractColleciton的clear方法
         public void clear() {
           //通過調用外部類的clear方法實現
           AbstractMap.this.clear();
         }
        //實現AbstractColleciton的contains方法
         public boolean contains(Object v) {
           //通過調用外部類的containsValue方法實現
           return AbstractMap.this.containsValue(v);
         }
       };
     }
     return values;
}

ketSet方法和values方法最終都是通過AbstractMap的entrySet方法實現的,通過entrySet方法獲得鍵值對的迭代器。

equals方法

該方法判斷兩個AbstractMap是否相同,通過比較每一個鍵值對來確認兩個AbstractMap是否相等,只有所有的鍵值對的鍵、值都相等,兩個AbstractMap才相等,源碼實現如下:

public boolean equals(Object o) {
    if (o == this)
      //若o是對象本身,返回true
      return true;
    if (!(o instanceof Map))
      //如果o不是一種Map,返回false
      return false;
    Map<K,V> m = (Map<K,V>) o;
    if (m.size() != size())
      //兩個AbstractMap的長度不相等,肯定不相同,返回false
      return false;
    try {
      Iterator<Entry<K,V>> i = entrySet().iterator();
      while (i.hasNext()) {
        //迭代本AbstractMap
        Entry<K,V> e = i.next();
        K key = e.getKey();
        V value = e.getValue();
        if (value == null) {
          //當前鍵值對的值爲null
          //如果m存在鍵爲key且對應的值不爲空的鍵值對,或者m沒有鍵爲key的鍵值對,說明這兩個Map不相等
          if (!(m.get(key)==null && m.containsKey(key)))
            return false;
        } else {
          //當前鍵值對的值不爲空,通過比較該值和另一個AbstractMap的鍵爲key對應的值是否相等即可
          if (!value.equals(m.get(key)))
            return false;
        }
      }
    } catch (ClassCastException unused) {
      return false;
    } catch (NullPointerException unused) {
      return false;
    }
    //所有判斷都通過,說明兩個AbstractMap相等
    return true;
}

hashCode方法

該方法計算該AbstractMap對象的哈希碼,源碼實現如下:

public int hashCode() {
    int h = 0;
    Iterator<Entry<K,V>> i = entrySet().iterator();
    while (i.hasNext())
      //將鍵值對哈希碼加起來
      h += i.next().hashCode();
    return h;
}

其實是將每個鍵值對的哈希碼加起來實現的。

toString方法

該方法實現比較簡單,不再說明

clone方法

該方法克隆一個本對象的拷貝,字段keySet和values都設置爲空,源碼實現如下:

protected Object clone() throws CloneNotSupportedException {
    //首先調用父類的clone方法
    AbstractMap<K,V> result = (AbstractMap<K,V>)super.clone();
    //字段都設置爲空
    result.keySet = null;
    result.values = null;
    return result;
}

該方法protected修飾,希望子類能夠重寫該方法。

以上介紹了AbstractMap的主要方法,該抽象類還定義了兩個靜態內部類: SimpleEntry和SimpleImmutableEntry類,這兩個類在EnumMap、IdentityHashMap和WeakHashMap使用的,以後研究這幾個類時再來說明這兩個靜態內部類。

參考

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