一、前言

  前面我们介绍了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. MSSQL Server数据库的四种连接方法和sql连接字符串

    MSSQL Server数据库的四种连接方法和sql连接字符串 分类: [ 03 ] C#(131) [ 07 ] SQL Server(68) [ 01 ] .NET(189) 今天用SQL Ser ...

  2. 关于C#中的线程重启的问题

    首先不管是C#也好,还是java也好,对于已经Abort的线程是无法再次Start的,除非是声明私有变量new一个新的线程,网上也有很多人说可以Suspend挂起线程,然后再Resume继续,但是相信 ...

  3. Java基础,输入输出

    package hanqi.test; import java.io.File; import java.io.IOException; public class Test02 { public st ...

  4. InstallShield Limited Edition for Visual Studio 2013 图文教程(教你如何打包.NET程序)

    InstallShield Limited Edition for Visual Studio 2013 图文教程(教你如何打包.NET程序) 标签: InstallShieldVS2013 2015 ...

  5. agularJs 路由

    angularJs的路由方式: 先定义一个模板ng-app-->然后定义路由的规则(routeProvider)在服务config里-->然后通过不同的URL实现 到单页面加载的所需页面的 ...

  6. C语言数组空间的初始化详解

    数组空间的初始化就是为每一个标签地址赋值.按照标签逐一处理.如果我们需要为每一个内存赋值,假如有一个int a[100];我们就需要用下标为100个int类型的空间赋值.这样的工作量是非常大的,我们就 ...

  7. 有jQuery背景,该如何用AngularJS编程思想?

    "我可以熟练使用jQuery进行客户端应用的开发,但是现在我希望开始使用Angular.js.哪位能描述一下这个过程中必要的模式变化吗?希望您的答案能够围绕下面这些具体的问题: 1. 我如何 ...

  8. 了解canvas

    目录 [1]HTML属性[2]CSS样式 [3]API 坐标 填充和描边 阴影 绘制矩形 绘制路径 绘制文本 绘制图像 使用图像 变换 合成 [4]DEMO 前面的话 canvas元素是HTML5最受 ...

  9. javascript语句——表达式语句、块语句、空语句和声明语句

    × 目录 [1]表达式 [2]块语句 [3]空语句[4]声明 前面的话 如果表达式在javascript中是短语,那么语句(statement)就是javascript整句或命令.表达式计算出一个值, ...

  10. [OpenCV] Samples 08: edge

    Canny edge detector 效率高,效果可控. TrackBar的使用. 技巧:gray找边缘后作为mask去CopyTo(). #include "opencv2/core/u ...