一、前言

  前面我们介绍了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是这样实现的:

 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;
}

  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,就像下面这样:

 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;
}

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

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

  明天会继续学习HashMap.

容器--Map和AbstractMap的更多相关文章

  1. C++关联容器<map>简单总结

    C++关联容器<map>简单总结 map提供大小可变的关联容器,基于关联键值高效检索元素值.当你处理键值对的数据是,都可以考虑使用map关联容器. 特点: 大小可变的关联容器,基于关联键值 ...

  2. 关联容器——map、set

    map类型通常被称为关联数组,与正常数组类似,不同之处在于其下标不必是整数.我们通过一个关键字而不是位置来查找值(键值对). 与之相对,set就是关键字的简单集合.当只是想知道一个值是否存在时,set ...

  3. c++中关联容器map的使用

    C++关联容器<map>简单总结(转) 补充: 使用count,返回的是被查找元素的个数.如果有,返回1:否则,返回0.注意,map中不存在相同元素,所以返回值只能是1或0. 使用find ...

  4. Java容器Map接口

    Map接口容器存放的是key-value对,由于Map是按key索引的,因此 key 是不可重复的,但 value 允许重复. 下面简单介绍一下Map接口的实现,包括HashMap,LinkedHas ...

  5. spring 中容器 map、set、list、property 的 bean 实例化

    参考:http://www.kaifajie.cn/spring/9966.html <bean id="fieldMap" class="org.springfr ...

  6. C++ 之关联容器 map

    标准库定义了四种关联容器:map是其中之一(另外还有set.multimap.multiset).map的元素以键-值(key-value),在学了顺序容器之后,再学习关联容器,就比较比较好理解了. ...

  7. 关联容器(map):支持高效查找的容器,一种键值对的集合。

    #include <iostream> #include <string> #include <map> #include <vector> using ...

  8. Java容器---Map基础

    1.Map API (1)Map 集合类用于存储元素对(称作"键"和"值"),其中每个键映射到一个值. java.util Interface Map<K ...

  9. java并发容器(Map、List、BlockingQueue)

    转发: 大海巨浪 Java库本身就有多种线程安全的容器和同步工具,其中同步容器包括两部分:一个是Vector和Hashtable.另外还有JDK1.2中加入的同步包装类,这些类都是由Collectio ...

随机推荐

  1. [常见问题]Project facet Java versin 1.8 is not support.

    发生这个问题的原因是我们的java编译环境(JDK版本),与tomcat运行环境(JDK或JRE版本)不一致导致的. 到eclipse的设置中找到compile项(或右键项目进入),看一下编译环境的J ...

  2. Java项目——模拟电话薄联系人增删改查

    该项目模拟了电话本记录联系人的业务功能,用来练习对数据库的增删改查等操作. 菜单类:Menu -- 用来封装主菜单和个选项的子菜单 Person类: Person--联系人的实体类 TelNoteRe ...

  3. iOS开发——UI精选OC篇&UIApplication,UIWindow,UIViewController,UIView(layer)简单介绍

    UIApplication,UIWindow,UIViewController,UIView(layer)简单介绍 一:UIApplication:单例(关于单例后面的文章中会详细介绍,你现在只要知道 ...

  4. java基础-复制

    package hanqi.test; import java.io.FileInputStream; import java.io.FileOutputStream; public class Te ...

  5. 【WP开发】正确理解页面缓存

    注:本文内容面向Runtime App. 在新建项目后,细心观察,你会发现在App类中有以下代码: // TODO: 将此值更改为适合您的应用程序的缓存大小 rootFrame.CacheSize = ...

  6. jQuery事件流的顺序

    <div id="aaron"> <div id='test'> <ul> <p>点击p被委托,ul被阻止了,因为内部重写了事件对象 ...

  7. 你的 mixin 兼容 ECMAScript 5 吗

    原文:Are your mixins ECMAScript 5 compatible? 作者:Nicholas C. Zakas 我最近在与客户合作的项目中,需要充分利用的 ECMAScript 5, ...

  8. mongodb 关系、引用、覆盖索引查询

    一.关系 MongoDB 的关系表示多个文档之间在逻辑上的相互联系.文档间可以通过嵌入和引用来建立联系.MongoDB 中的关系可以是:1对1,1对多,多对1,多对多. 一个用户可以用多个地址,这是典 ...

  9. office 2010 安装教程

    Microsoft Office 2010,是微软推出的新一代办公软件,提供了一些更丰富和强大的新功能,开发代号为Office 14,实际是第12个发行版.该软件共有6个版本,分别是初级版.家庭及学生 ...

  10. 整理的一些PHP面试题目

    1.strlen()和mb_strlen()的作用分别是什么? strlen()和mb_strlen()的作用都是来获取字符串的长度,其中strlen()只针对单字节编码字符,也就是计算字符串的总字节 ...