一、前言

  前面我们介绍了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. [Spring框架]Spring AOP基础入门总结一.

    前言:前面已经有两篇文章讲了Spring IOC/DI 以及 使用xml和注解两种方法开发的案例, 下面就来梳理一下Spring的另一核心AOP. 一, 什么是AOP 在软件业,AOP为Aspect ...

  2. 学习ASP.NET MVC(九)——“Code First Migrations ”工具使用示例

    在上一篇文章中,我们学习了如何使用实体框架的“Code First Migrations ”工具,使用其中的“迁移”功能对模型类进行一些修改,同时同步更新对应数据库的表结构. 在本文章中,我们将使用“ ...

  3. 【java并发】传统线程技术中创建线程的两种方式

    传统的线程技术中有两种创建线程的方式:一是继承Thread类,并重写run()方法:二是实现Runnable接口,覆盖接口中的run()方法,并把Runnable接口的实现扔给Thread.这两种方式 ...

  4. 【原创】高性能网络编程(二):上一个10年,著名的C10K并发连接问题

    1.前言 对于高性能即时通讯技术(或者说互联网编程)比较关注的开发者,对C10K问题(即单机1万个并发连接问题)应该都有所了解."C10K"概念最早由Dan Kegel发布于其个人 ...

  5. CSS3 transform 属性详解(skew, rotate, translate, scale)

    写这篇文章是因为在一个前端QQ群里,网友 "小豆豆" (应他要求要出现他的网名......) ,问skew的角度怎么算,因为他看了很多文章还是不能理解skew的原理.于是,我觉得有 ...

  6. seajs模块化开发

    seajs是一个起辅助作用的库,所以它可以更方便开发,而它可以解决以下问题: 1.命名问题,就是冲突 2.性能问题,就是只要一个功能,但却使用一个大插件中的一个小功能,所以要手动拆分出这个功能 3.j ...

  7. maven项目部署打包

    方法一.把maven依赖的jar包一起打包 http://maven.apache.org/plugins/maven-assembly-plugin/usage.html pom/build中加入以 ...

  8. 通过js获得选择文件的绝对路径

    <form name="thisform" method="get" action="test.jsp" id="thisf ...

  9. ASP.NET MVC 4.0中选择Windows 验证默认出错拒绝访问的原因和解决方案

    在VS 2012或者2013 中,根据模板创建一个ASP.NET MVC 4.0的应用程序,选择下面的模板 然后选择Intranet Application 不对源代码做任何修改,直接按下F5调试,会 ...

  10. Hadoop官方文档翻译—— YARN ResourceManager High Availability 2.7.3

    ResourceManager High Availability (RM高可用) Introduction(简介) Architecture(架构) RM Failover(RM 故障切换) Rec ...