Map接口和AbstractMap抽象类详解

来源:互联网 发布:python arma 编辑:程序博客网 时间:2024/06/03 12:56

为了更好的理解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源码
原创粉丝点击