• 容器--Map和AbstractMap


    一、前言

      前面我们介绍了Collection接口的定义及一系列实现,并重点介绍了List体系下的一些实现,对于Collection来说,其实还有Set系列同样很重要,但由于Set是依赖于Map实现的,所以我们在这里先介绍Map.

      Collection的特点是存储一类元素的集合,而Map则描述了一组映射关系的集合,即我们常说的key-value结构。这类容器的特点是容器中的每一项都是一个键值对,键的值不能重复,但允许有null键和null值的存在,一个键只能对应一个值,但多个不同的键可以对应于同一个值。

      由于键值的惟一性,所以其key集合就天然是一个Set, 因此,JDK的set实现也正是基于这样的思想,使用了Map中的key集合来实现。

    二、主要方法

      虽然存储的元素不一样,但同为集合,Map和Collection之间还是有很多功能相似甚至是相同的功能,下面一个表格进行对比如下:

    Map和Collection接口的方法对比
    方法描述 Map方法名 Collection方法名
    容器中元素个数 size() size()
    判断容器是否为空 isEmpty() isEmpty()
    添加元素 put(K key, V value) add(E e)
    批量添加元素

    putAll(Map<K, V> map)

    addAll(Collection<E> collection)
    删除元素 remove(Object key)

    remove(Object obj)

    removeAll(Collection<E> collection)

    retainAll(Collection<E> collection)

    清空列表 clear() clear()
    判断元素是否存在

    containsKey(Object key)

    containsValue(Object value)

    contains(Object e)

    containsAll(Collection<E> e)

    遍历

    keySet()

    entrySet()

    values()

    iterator()
    比较 equals(Object obj) equals(Object o)
    求hash hashCode() hashCode()
    根据key查找value V get(Object key) 无对应方法
    转化为数组 无对应方法

    toArray()

    toArray(T[] t)

     

      从上表我们可以看到,这两个容器都提供了对容器的增加,修改,删除及遍历的功能,对于Map来说,由于其特有的映射结构提供了根据key查找value的功能,而Collection由于是一组类型相同的元素,所以提供了转化为toArray的方法。

    三、AbstractMap的实现原理

      AbstractMap作为Map的抽象实现类,提供了绝大多数方法的实现。其中的惟一个未实现的方法是entrySet()方法,返回一个元素类型为Map.Entry的Set.

      1)由于Set属于Collection,通过上表的比较我们可以知道Collection和Map的绝大部分功能是相似的,所以对于这些功能的实现就可以根据entrySet的相应方法来实现。比如对于删除方法,JDK是这样实现的:

     1 public V remove(Object key) {
     2         Iterator<Entry<K,V>> i = entrySet().iterator();
     3         Entry<K,V> correctEntry = null;
     4         if (key==null) {
     5             while (correctEntry==null && i.hasNext()) {
     6                 Entry<K,V> e = i.next();
     7                 if (e.getKey()==null)
     8                     correctEntry = e;
     9             }
    10         } else {
    11             while (correctEntry==null && i.hasNext()) {
    12                 Entry<K,V> e = i.next();
    13                 if (key.equals(e.getKey()))
    14                     correctEntry = e;
    15             }
    16         }
    17 
    18         V oldValue = null;
    19         if (correctEntry !=null) {
    20             oldValue = correctEntry.getValue();
    21             i.remove();
    22         }
    23         return oldValue;
    24     }

      2)不支持put方法的实现,相应的,putAll方法由于调用了put,所以这个方法也不支持。

      3)根据定义,map中的key和value都有可能为null,所以判断一个map中是否包含某个key,不应该根据get(key) == null来判断 ,而应该调用containsKey(key)来判断。

      4)keySet()和values()使用了匿名内部类的方式来实现,主要的实现逻辑是重写了抽象容器类的iterator方法,将其遍历转化为对于key和值的遍历。

    三、总结

      AbstractMap通过将entrySet定义为抽象方法的方式,巧妙的将具体的实现和存储逻辑交给子类实现,这样实现者可以专注于元素的存储,另外,通过学习AbstractMap中的方法实现,我们可以借鉴其map的遍历方式,正确的应该是先找到entrySet,就像下面这样:

     1 public boolean containsKey(Object key) {
     2         Iterator<Map.Entry<K,V>> i = entrySet().iterator();
     3         if (key==null) {
     4             while (i.hasNext()) {
     5                 Entry<K,V> e = i.next();
     6                 if (e.getKey()==null)
     7                     return true;
     8             }
     9         } else {
    10             while (i.hasNext()) {
    11                 Entry<K,V> e = i.next();
    12                 if (key.equals(e.getKey()))
    13                     return true;
    14             }
    15         }
    16         return false;
    17     }

     在遍历时,我们使用iterator(), 而不是使用for(String key : map.keySet())的方式。因为这种方式的问题在于,如果按抽象类的实现方式,map.keySet()相当于是需要对整个map进行遍历,而在for循环中, 对于map.get(key)来说,实际上又需要遍历整个map,循环多少次,就遍历多少次,这确实是非常低效率的。

    当然,也许HashMap并不会在get(key)时遍历整个map,但这并不能保证其它的实现不是这样,所以,iterator才是正理。

      明天会继续学习HashMap.

     

  • 相关阅读:
    想让进程后台运行,试试Linux的nohup命令,3分钟学会。
    面试官:你能说一下Redis的常见应用场景吗?
    面试被问MySQL 主从复制,怎么破?
    Spring Boot 解决跨域问题的 3 种方案!
    Kafka如果丢了消息,怎么处理的?
    惊呆,这样操作 Nginx 并发数就能达到3w?
    easyexcel 自动设置列宽(转)
    Ubuntu18.04自适应VMware调整桌面大小
    IDEA将maven项目打包时同时带上项目的maven依赖(转)
    python 定时框架APScheduler
  • 原文地址:https://www.cnblogs.com/macs524/p/5738990.html
Copyright © 2020-2023  润新知