公子姓王

导航

Java容器解析系列(10) Map AbstractMap 详解

前面介绍了ListQueue相关源码,这篇开始,我们先来学习一种java集合中的除Collection外的另一个分支------Map,这一分支的类图结构如下:

这里为什么不先介绍Set相关:因为很多Set实现类是通过对应的Map来实现,使用Map中key不能重复的特性,往Set中add元素的本质就是Map的put(),这里先介绍Map,后续介绍Set的时候再做具体介绍

首先,我们来看Map接口:

/**
  Map将key映射到value(存储键值对);
  一个map不能存在重复的key;
  一个key最多可以映射到1个value;
  这个接口用来替换Dictionary抽象类;
 * @since 1.2
 */
public interface Map<K,V> {
    // Query Operations
    int size();
    boolean isEmpty();
    boolean containsKey(Object key);
    boolean containsValue(Object value);

	// 按照指定键获得相对应的值;
	// 如果不能存在指定键值对则返回null;
    V get(Object key);

    // Modification Operations
    
	// 可选操作;添加指定键值对
    V put(K key, V value);

    // 可选操作;
    // 移除指定键值对;
    // 返回移除前key对应的value;
    V remove(Object key);

    // Bulk Operations
    
    void putAll(Map<? extends K, ? extends V> m);
    void clear();

    // Views
    
	// 返回Map中所有key的视图
    Set<K> keySet();
    // 返回Map中所有value的视图
    Collection<V> values();
    // 返回Map中所有的Entry的视图,也即key-value的视图
    Set<Map.Entry<K, V>> entrySet();

    // 一个Entry就是一个键值对;
    interface Entry<K,V> {
        K getKey();
        V getValue();
        V setValue(V value);
        boolean equals(Object o);
        int hashCode();
    }

    // Comparison and hashing

    boolean equals(Object o);
    int hashCode();

}

和所有集合接口一样,Map接口主要是定义键值对相关的增删改查的操作,考虑到实现不可修改的Map的实现,其中增加(其实也包括修改)和删除都是可选操作.

Map接口中必须实现的方法主要是围绕着查找遍历Map:

Map中提供了3种遍历方式:

  1. 使用keySet()获取所有key的集合,然后用集合里面的可以获取所有value;
  2. 使用values() 获取所有value的集合;
  3. 使用entrySet()获取所有key-value的集合;
    其使用代码案例如下:
    Set set = map.keySet();
    for (Object key : set) {
        System.out.println(map.get(key));
    }
    Collection values = map.values();
    Iterator iterator = values.iterator();
    while (iterator.hasNext()){
        System.out.println("value " + iterator.next());
    }
    Set entrySet = map.entrySet();
    for (Object o : entrySet) {
        Map.Entry entry = (Map.Entry) o;
        System.out.println(entry);      //key=value
        System.out.println(entry.getKey() + " / " + entry.getValue());
    }

和之前介绍的集合类一样,Map相关也提供了一个AbstractMap抽象类,提供Map的骨架实现,其所有功能都是通过entrySet()来实现,下面是其源代码:

为了简略篇幅,这里只列出部分具代表性的方法源码,具体可以参考jdk源码

/**
 * 提供Map接口的骨架实现;
 * 如果要实现一个不可修改的map,只需要继承该类,并实现entrySet方法,其返回的Set应不可修改;
 * @since 1.2
 */
public abstract class AbstractMap<K, V> implements Map<K, V> {

    protected AbstractMap() {}

    // Query Operations

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

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

    public boolean containsValue(Object value) {
        // ...
    }

    public boolean containsKey(Object key) {
        // ...
    }
	
    // 通过entrySet()来实现,其实所有的方法都是通过entrySet()来实现
    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;
    }

    // Modification Operations
	
    // 默认不允许添加键值对
    public V put(K key, V value) {
        throw new UnsupportedOperationException();
    }

    // 通过entrySet()实现,如果entrySet()不支持移除,其remove()会抛出UnsupportedOperationException
    public V remove(Object key) {
        Iterator<Entry<K,V>> i = entrySet().iterator();
        Entry<K,V> correctEntry = null;
        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();
            i.remove();
        }
        return oldValue;
    }

    // Bulk Operations

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

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

    // Views

    transient volatile Set<K> keySet = null;
    transient volatile Collection<V> values = null;

    public Set<K> keySet() {
        // ...
    }

    public Collection<V> values() {
        // ...
    }
	
    // entrySet()仍然为抽象方法,待子类实现
    public abstract Set<Entry<K, V>> entrySet();

    // Comparison and hashing

    public boolean equals(Object o) {
        // ...
    }

    public int hashCode() {
        // ...
    }

    public String toString() {
        // ...
    }

    protected Object clone() throws CloneNotSupportedException {
        // ...
    }

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

除了上面的之外,在jdk1.6之后,AbstractMap还提供了SimpleEntrySimpleImmutableEntry两个静态内部类,其均实现了Map.Entry接口,两者的唯一区别为SimpleImmutableEntry不支持修改key对应的value,其setValue()如下:

public V setValue(V value) {
	throw new UnsupportedOperationException();
}

SimpleEntry的源代码如下,没有什么需要特别讲解的:

/**
 * @since 1.6
 */
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;
    }

}

posted on 2018-10-15 09:17  公子姓王  阅读(433)  评论(0编辑  收藏  举报