zoukankan      html  css  js  c++  java
  • 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;
        }
    
    }
    
  • 相关阅读:
    洛谷 P2330 [SCOI2005]繁忙的都市
    2016-2017 ACM-ICPC, Asia Tsukuba Regional Contest D Hidden Anagrams
    HDU1792A New Change Problem(GCD规律推导)
    HDU1222Wolf and Rabbit(GCD思维)
    poj2635The Embarrassed Cryptographer(同余膜定理)
    poj3270Cow Sorting(置换+贪心)
    计数排序(O(n+k)的排序算法,空间换时间)
    POJ1222EXTENDED LIGHTS OUT(高斯消元)
    BZOJ 2038: [2009国家集训队]小Z的袜子(hose) (莫队算法)
    2301: [HAOI2011]Problem b ( 分块+莫比乌斯反演+容斥)
  • 原文地址:https://www.cnblogs.com/jamesvoid/p/9779167.html
Copyright © 2011-2022 走看看