前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java 集合框架(4)---- Map 相关类解析(上)

Java 集合框架(4)---- Map 相关类解析(上)

作者头像
指点
发布2019-01-18 11:39:54
4590
发布2019-01-18 11:39:54
举报
文章被收录于专栏:指点的专栏指点的专栏

前言

有一段时间不更新博客了。这段时间确实事挺多的,不过很开心的是这个暑假找到了第一份和自己所学专业对口的实习工作。不过租房的时候真是费了挺大劲,走了挺多弯路,只想说城市套路深。。。

好了,步入正题,上篇文章中我们一起看了一下 List 接口的相关具体类(ArrayListLinkedList ….),这篇开始我们开始探索 Java 集合框架中的 Map 接口及其相关子类。可能有些小伙伴会问了:为什么不先讲 Set 接口而讲 Map 接口呢?确实在集合框架的第一篇文章中我介绍接口的顺序是先 ListSet 然后才是 Map 接口,不过在这里还是决定先讲 Map 接口,因为 Set 接口下的一些具体类(HashSet ….)是通过 Map 接口下的一些具体类(HashMap)实现的,而 Map 接口中具体类却不是通过 Set 接口(有些许依赖,但是主要逻辑上不是)来实现的。所以我们掌握了 Map 接口的一些具体类之后,再去看 Set 接口就很容易上手了。好了, 老规矩,先来看一下 Map 接口的继承关系图:

这里写图片描述
这里写图片描述

好吧,我偷懒了,还是用的集合框架第一篇文章的那个图,不过我们现在只需要关注 Map 接口了,可以看到,Map 接口是独立存在的,我们之前看的 List 接口是继承于 Collection 接口的子接口。但是 Map 接口并不依赖 Collection 接口。关于 Map 接口的一些基本概念在 Java 集合框架 (1)— 概述 中已经介绍过了。下面来看一下 Map 接口下的相关类和接口:

AbstractMap

从上面的图中我们知道这个类是一个抽象类,还是先从官方对它的描述开始:

This class provides a skeletal implementation of the Map interface, to minimize the effort required to implement this interface. To implement an unmodifiable map, the programmer needs only to extend this class and provide an implementation for the entrySet method, which returns a set-view of the map’s mappings. Typically, the returned set will, in turn, be implemented atop AbstractSet. This set should not support the add or remove methods, and its iterator should not support the remove method.

To implement a modifiable map, the programmer must additionally override this class’s put method (which otherwise throws an UnsupportedOperationException), and the iterator returned by entrySet().iterator() must additionally implement its remove method.

The programmer should generally provide a void (no argument) and map constructor, as per the recommendation in the Map interface specification.

The documentation for each non-abstract method in this class describes its implementation in detail. Each of these methods may be overridden if the map being implemented admits a more efficient implementation.

大概意思是: 这个类提供了 Map 接口的骨架实现,以最小化实现 Map 接口功能所需的要求。 如果要实现一个不可更改的 map 对象,开发者只需要继承这个类并实现 entrySet 方法,返回一个包含当前 Map 对象中所有键值对的集合。通常,这个集合应该基于 AbstractSet 类来实现,并且不应该支持添加和删除元素的方法,其迭代器不应该支持移除元素的方法。 如果要实现可更改的 map 对象,开发者必须重写 put 方法(默认抛出 UnsupportedOperationException 异常),并且通过 entrySet().iterator() 方法返回的迭代器必须实现移除元素的方法。 开发者应该提供一个无参构造方法,和接受另一个 map 对象的作为参数的构造方法。 这个文档描述了每个非 abstract 方法的实现细节,在继承过程中,如果对应方法有更适应当前类的实现,我们应该重写这些方法,并添加更好的实现逻辑。

有了基本的了解之后,我们再来看看这个类的部分源码 AbstractMap.java

代码语言:javascript
复制
public abstract class AbstractMap<K,V> implements Map<K,V> {

    protected AbstractMap() {
    }

    // Query Operations

    /**
     * 返回当前 map 中键值对元素的数目
     */
    public int size() {
        return entrySet().size();
    }

    /**
     * 判断当前 map 是否已经没有任何键值对元素
     */
    public boolean isEmpty() {
        return size() == 0;
    }

    /**
     * 判断参数所给 值 是否存在当前 map 中的某一个键值对元素中(通过 equals 方法判断),
     * 如果存在,返回 true,否则返回 false
     */
    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;
    }

    /**
     * 和上个方法类似,判断参数所给 键 是否存在当前 map 中的某一个键值对元素中(通过 equals 方法判断),
     * 如果存在,返回 true,否则返回 false
     */
    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;
    }

    /**
     * 获取参数所给的 键 所对应的值,如果当前 map 中不存在这个键,那么返回 null,
     * 这是默认的实现,通过迭代器遍历,效率低,不同的实体类都会重写该方法
     */
    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

    /**
     * 在当前 map 中存入一个新的键值对元素,默认抛出 UnsupportedOperationException 异常,
     * 即操作不支持
     */
    public V put(K key, V value) {
        throw new UnsupportedOperationException();
    }

    /**
     * 从当前 map 中移除参数代表的 键 所对应的键值对元素,并返回对应的 值
     * 通过迭代器找到对应键值对元素,然后调用迭代器的 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

    /**
     * 将参数所代表的 map 对象中所有的键值对元素放入当前 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());
    }

    /**
     * 移除当前 map 中的所有键值对元素
     */
    public void clear() {
        entrySet().clear();
    }


    // Views

    /**
     * 储存键和值的集合
     * Each of these fields are initialized to contain an instance of the
     * appropriate view the first time this view is requested.  The views are
     * stateless, so there's no reason to create more than one of each.
     */
    transient volatile Set<K>        keySet;
    transient volatile Collection<V> values;

    /**
     * 返回一个包含了当前 map 对象中所有的 “键” 的 Set 对象,
     * 返回一个匿名内部类对象,其实现了 Set 接口的基本功能
     */
    public Set<K> keySet() {
        if (keySet == null) {
            keySet = new AbstractSet<K>() {
                public Iterator<K> iterator() {
                    return new Iterator<K>() {
                        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();
                        }
                    };
                }

                public int size() {
                    return AbstractMap.this.size();
                }

                public boolean isEmpty() {
                    return AbstractMap.this.isEmpty();
                }

                public void clear() {
                    AbstractMap.this.clear();
                }

                public boolean contains(Object k) {
                    return AbstractMap.this.containsKey(k);
                }
            };
        }
        return keySet;
    }

    /**
     * 返回一个包含了当前 map 对象中所有的 “值” 的 Collection 对象,
     * 返回一个匿名内部类对象,其实现了 Collection 接口的基本功能
     */
    public Collection<V> values() {
        if (values == null) {
            values = new AbstractCollection<V>() {
                public Iterator<V> iterator() {
                    return new Iterator<V>() {
                        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();
                        }
                    };
                }

                public int size() {
                    return AbstractMap.this.size();
                }

                public boolean isEmpty() {
                    return AbstractMap.this.isEmpty();
                }

                public void clear() {
                    AbstractMap.this.clear();
                }

                public boolean contains(Object v) {
                    return AbstractMap.this.containsValue(v);
                }
            };
        }
        return values;
    }

    // 抽象方法,返回储存了当前 map 对象的所有键值对元素的 Set 对象
    public abstract Set<Entry<K,V>> entrySet();


    // Comparison and hashing

    /**
     * 比较当前 map 对象和参数所指定的 map 对象,
     * 如果当前 map 对象中所有的键值对元素和参数所指定的 map 对象
     * 中的所有键值对元素都相同(通过 equals 方法判断)
     */
    public boolean equals(Object o) {
        if (o == this)
            return true;

        if (!(o instanceof Map))
            return false;
        Map<?,?> m = (Map<?,?>) o;
        if (m.size() != size())
            return false;

        try {
            Iterator<Entry<K,V>> i = entrySet().iterator();
            while (i.hasNext()) {
                Entry<K,V> e = i.next();
                K key = e.getKey();
                V value = e.getValue();
                if (value == null) {
                    if (!(m.get(key)==null && m.containsKey(key)))
                        return false;
                } else {
                    if (!value.equals(m.get(key)))
                        return false;
                }
            }
        } catch (ClassCastException unused) {
            return false;
        } catch (NullPointerException unused) {
            return false;
        }

        return true;
    }

    /**
     * 重写 Object 类的方法, 返回当前 map 对象的 hash 值
     */
    public int hashCode() {
        int h = 0;
        Iterator<Entry<K,V>> i = entrySet().iterator();
        while (i.hasNext())
            h += i.next().hashCode();
        return h;
    }

    /**
     * 重写 Object 的方法,返回当前 map 对象的 String 对象表示
     */
    public String toString() {
        Iterator<Entry<K,V>> i = entrySet().iterator();
        if (! i.hasNext())
            return "{}";

        StringBuilder sb = new StringBuilder();
        sb.append('{');
        for (;;) {
            Entry<K,V> e = i.next();
            K key = e.getKey();
            V value = e.getValue();
            sb.append(key   == this ? "(this Map)" : key);
            sb.append('=');
            sb.append(value == this ? "(this Map)" : value);
            if (! i.hasNext())
                return sb.append('}').toString();
            sb.append(',').append(' ');
        }
    }

    /**
     * 重写 Object 类的方法,返回一个当前 map 对象的复制对象,
     * 键值对不会被复制
     */
    protected Object clone() throws CloneNotSupportedException {
        AbstractMap<?,?> result = (AbstractMap<?,?>)super.clone();
        result.keySet = null;
        result.values = null;
        return result;
    }

    // ...

}

和我们之前文章中介绍的 AbstractList 很类似,利用 Java 多态的特性,提供了对应接口的基本骨架实现,而其他的扩展功能留给子类去实现,我们从开头的图中也知道,图中的 Map 接口下的具体类都是继承于这个类,即为这个类的子类。

SortedMap

下面我们来看 Map 接口下的另外一个接口 SortedMap ,这个接口官方对它的描述文档有点长,在这里就不贴了,总结一下这个接口的主要功能就是声明一些方法,用于给实现这个接口的容器指定一个约定: 实现这个接口的容器应该要按某个规则对容器内的元素进行排序,并且可以通过这个接口提供的方法获取容器特定的一些元素。但是接口本身不干预容器的排序规则,具体的排序方式由容器自己决定。 我们来看一下这个接口的源码 SortedMap.java:

代码语言:javascript
复制
public interface SortedMap<K,V> extends Map<K,V> {
    /**
     * 返回这个 map 对象中用于通过 “键” 来对元素进行排序的 Comparator(比较器)对象,
     * 如果当前 map 对象使用 “键” 的自然升序规则排序元素(即未指定排序所用的 Comparator 对象),
     * 那么返回 null 
     */
    Comparator<? super K> comparator();

    /**
     * 获取当前 map 对象中元素 “键” 的范围在 [fromKey, toKey) 之中的键值对元素,
     * 将这些键值对放在一个 SortedMap 对象中并返回
     */
    SortedMap<K,V> subMap(K fromKey, K toKey);

    /**
     * 获取当前 map 中 “键” 小于 toKey 的键值对元素,
     * 将这些键值对放在一个 SortedMap 对象中并返回
     */
    SortedMap<K,V> headMap(K toKey);

    /**
     * 获取当前 map 中 “键” 不小于fromKey 的键值对元素,
     * 将这些键值对放在一个 SortedMap 对象中并返回
     */
    SortedMap<K,V> tailMap(K fromKey);

    /**
     * 返回当前 map 中的第一个 键
     */
    K firstKey();

    /**
     * 返回当前 map 中的最后一个 键
     */
    K lastKey();

    /**
     * 返回一个 Set 对象,其中元素为当前 map 的键值对中的 “键”,
     * 元素顺序按当前 map 对象的排序规则对 “键” 升序的规则排列
     */
    Set<K> keySet();

    /**
     * 返回一个 Collection 对象,其中元素为当前 map 的键值对中的 “值”,
     * 元素顺序按当前 map 对象的排序规则对 “键” 升序的规则排列
     */
    Collection<V> values();

    /**
     * 返回一个 Set 对象,其中元素为当前 map 中的所有键值对元素,
     * 元素顺序按当前 map 对象的排序规则对 “键” 升序的规则排列
     */
    Set<Map.Entry<K, V>> entrySet();
}

我们之后将会看到,Map 接口中的具体类 TreeMap 就是一个实现了 SortedMap 接口(实现的是 NavigableMap 接口,NavigableMap 继承了 SortedMap 接口)方法的类。因此我们已经可以知道 TreeMap 是一个按照某个排序规则对 “键” 进行比较并以此作为依据来对键值对元素进行排序的 map 容器。关于这个类的具体实现细节,在之后的文章中我们会一起探索。

好了,在这篇文章中我们看了两个 Map 接口下的抽象子类(AbstractMap)和接口(SortedMap),我们开发中常见的一些具体类(HashMapTreeMapWeakHashMapIdentityHashMap …..)也是继承了这个抽象类,并且某些类(TreeMap…..)也是实现了 SortedMap 接口。这些类将会在之后的篇幅介绍。 如果博客中有什么不正确的地方,还请多多指点。如果这篇文章对您有帮助,请不要吝啬您的赞,欢迎继续关注本专栏。

谢谢观看。。。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018年08月05日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
    • AbstractMap
      • SortedMap
      相关产品与服务
      容器服务
      腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档