Java集合类源码解析:AbstractMap
引言
今天学习一个Java集合的一个抽象类 AbstractMap ,AbstractMap 是Map接口的 实现类之一,也是HashMap、TreeMap、ConcurrentHashMap 等的父类,它提供了Map 接口中方法的基本实现(关于Map接口有疑惑的同学可参考 Java集合类根接口:Collection 和 Map)

源码解析
因为 AbstractMap 类是实现Map接口的抽象类,所以其内部也包含了操作子元素的实体接口 Entry,其源码方法对元素的操作都是基于 Entry 的视图实现的。
抽象函数entrySet()
AbstractMap类中有一个唯一的抽象函数 entrySet() ,类中对集合视图操作的很多方法都是依赖这个抽象函数的,它返回一个保存所有 key-value 映射的Set。
当我们要实现一个不可变的 Map 时,只需要继承 AbstractMap 类并实现 entrySet() 即可。
如果想要实现一个可变的 Map ,我们还需要重写 put() 方法,因为 AbstractMap 类中默认不支持 put实现,子类必须重写该方法的实现,否则会抛出异常:
public V put(K key, V value) {
throw new UnsupportedOperationException();
}
在这里,有人会疑惑为什么必须重写 put 方法呢,很大可能是官方考虑到也许会有不可修改的Map实现子类继承 AbstractMap,如果 put 方法默认可以操作,那不可修改的子类就行不通了。
两个集合视图
AbstractMap没有提供 entrySet() 的实现,但是却提供了 keySet() 与 values() 集合视图的默认实现,它们都是依赖于 entrySet() 返回的集合视图实现的,这是他们的源码:
- keySet()
// 返回一个AbstractSet的实现,包含了所有的key
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;
}
- values()
// 返回一个AbstractCollection的实现,包含了所有的value
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;
}
操作方法
下面看看 AbstractMap 的具体操作集合的方法。
- 添加
/**
* 没有提供实现,子类必须重写该方法,否则调用put()会抛出异常。
*/
public V put(K key, V value) {
throw new UnsupportedOperationException();
}
/**
* 遍历一个Map,然后将每一个键值对put到该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());
}
- 删除
/**
* 遍历entrySet,先找到对应的key的entry,然后删除。
*/
public V remove(Object key) {
Iterator<Entry<K,V>> i = entrySet().iterator();
Entry<K,V> correctEntry = null;
//遍历查找,当某个 Entry 的 key 和 指定 key 一致时结束
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();
//调用迭代器的 remove 方法
i.remove();
}
return oldValue;
}
/**
* 清空entrySet,等价于清空该Map。
*/
public void clear() {
entrySet().clear();
}
- 查询对应的子元素
//遍历entrySet(),看看是否包含参数key
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;
}
//与containsKey方法同理,只是比较的是value
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;
}
- 获取元素
//使用 entrySet 迭代器进行遍历,根据 key 查找,返回对应的value
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;
}
两个子类
除了上面的方法之外,AbstractMap 类中还提供了两个子类,分别是 SimpleEntry,SimpleImmutableEntry,两个子类都实现了Map.Entry 以及 Serializable 接口,这是他们的源码
- SimpleEntry
public static class SimpleEntry<K, V> implements Entry<K, V>, Serializable {
private static final long serialVersionUID = -8499721149061103585L;
private final K key;
private V value;
public SimpleEntry(K var1, V var2) {
this.key = var1;
this.value = var2;
}
public SimpleEntry(Entry<? extends K, ? extends V> var1) {
this.key = var1.getKey();
this.value = var1.getValue();
}
public K getKey() {
return this.key;
}
public V getValue() {
return this.value;
}
public V setValue(V var1) {
Object var2 = this.value;
this.value = var1;
return var2;
}
public boolean equals(Object var1) {
if (!(var1 instanceof Entry)) {
return false;
} else {
Entry var2 = (Entry)var1;
return AbstractMap.eq(this.key, var2.getKey()) && AbstractMap.eq(this.value, var2.getValue());
}
}
public int hashCode() {
return (this.key == null ? 0 : this.key.hashCode()) ^ (this.value == null ? 0 : this.value.hashCode());
}
public String toString() {
return this.key + "=" + this.value;
}
}
- SimpleImmutableEntry
public static class SimpleImmutableEntry<K, V> implements Entry<K, V>, Serializable {
private static final long serialVersionUID = 7138329143949025153L;
private final K key;
private final V value;
public SimpleImmutableEntry(K var1, V var2) {
this.key = var1;
this.value = var2;
}
public SimpleImmutableEntry(Entry<? extends K, ? extends V> var1) {
this.key = var1.getKey();
this.value = var1.getValue();
}
public K getKey() {
return this.key;
}
public V getValue() {
return this.value;
}
public V setValue(V var1) {
throw new UnsupportedOperationException();
}
public boolean equals(Object var1) {
if (!(var1 instanceof Entry)) {
return false;
} else {
Entry var2 = (Entry)var1;
return AbstractMap.eq(this.key, var2.getKey()) && AbstractMap.eq(this.value, var2.getValue());
}
}
public int hashCode() {
return (this.key == null ? 0 : this.key.hashCode()) ^ (this.value == null ? 0 : this.value.hashCode());
}
public String toString() {
return this.key + "=" + this.value;
}
}
两个实现都非常简单,具体的方法也是大同小异,唯一有区别的是 setValue 这个方法,SimpleEntry 支持 setValue 的操作实现,而 SimpleImmutableEntry 就没有实现,说明前者为可变集合,后者为不可变集合。
参考:
http://www.importnew.com/29686.html
Java集合类源码解析:AbstractMap的更多相关文章
- Java集合类源码解析:Vector
[学习笔记]转载 Java集合类源码解析:Vector 引言 之前的文章我们学习了一个集合类 ArrayList,今天讲它的一个兄弟 Vector.为什么说是它兄弟呢?因为从容器的构造来说,Vec ...
- Java集合类源码解析:HashMap (基于JDK1.8)
目录 前言 HashMap的数据结构 深入源码 两个参数 成员变量 四个构造方法 插入数据的方法:put() 哈希函数:hash() 动态扩容:resize() 节点树化.红黑树的拆分 节点树化 红黑 ...
- Java集合类源码解析:ArrayList
目录 前言 源码解析 基本成员变量 添加元素 查询元素 修改元素 删除元素 为什么用 "transient" 修饰数组变量 总结 前言 今天学习一个Java集合类使用最多的类 Ar ...
- Java集合类源码解析:LinkedHashMap
前言 今天继续学习关于Map家族的另一个类 LinkedHashMap .先说明一下,LinkedHashMap 是继承于 HashMap 的,所以本文只针对 LinkedHashMap 的特性学习, ...
- Java集合类源码解析:AbstractList
今天学习Java集合类中的一个抽象类,AbstractList. 初识AbstractList AbstractList 是一个抽象类,实现了List<E>接口,是隶属于Java集合框架中 ...
- 【转】Java HashMap 源码解析(好文章)
.fluid-width-video-wrapper { width: 100%; position: relative; padding: 0; } .fluid-width-video-wra ...
- JDK8集合类源码解析 - HashSet
HashSet 特点:不允许放入重复元素 查看源码,发现HashSet是基于HashMap来实现的,对HashMap做了一次“封装”. private transient HashMap<E,O ...
- Java——LinkedHashMap源码解析
以下针对JDK 1.8版本中的LinkedHashMap进行分析. 对于HashMap的源码解析,可阅读Java--HashMap源码解析 概述 哈希表和链表基于Map接口的实现,其具有可预测的迭 ...
- Java - TreeMap源码解析 + 红黑树
Java提高篇(二七)-----TreeMap TreeMap的实现是红黑树算法的实现,所以要了解TreeMap就必须对红黑树有一定的了解,其实这篇博文的名字叫做:根据红黑树的算法来分析TreeMap ...
随机推荐
- iptables log日志记录功能扩展应用:iptables自动配置临时访问策略,任意公网登录服务器
一.修改日志记录: 1. 修改配置文件: vi /etc/rsyslog.conf 添加以下内容 #iptables log kern.=notice /var/log/iptables.log 2. ...
- 1.1.5 PROB Friday the Thirteenth
Friday the Thirteenth Is Friday the 13th really an unusual event? That is, does the 13th of the mont ...
- 13.缓存、三级缓存、内存溢出、AsyncTask
SharePreference工具类 /** * SharePreference封装 * */ public class PrefUtils { public static final String ...
- ruby-super用法
ruby语法-super用法 本文主要介绍ruby中super方法的使用.super方法参数传递.method执行顺序. 下面主要通过实例来说明super方法的使用: 示例1: #!/usr/bin/ ...
- 关键字New,如阴魂不散
本文是溪源翻译的第一篇技术文章,查看原文,因时间仓促,也许翻译过程措辞不当,还请见谅. 当你使用类似于C#或VisualBasic这种强类型语言,在实例化对象时,你往往会使用New这个关键字.我敢保证 ...
- C语言中关键字restrict的概念,使用范围,例子
概念: restrict,C语言中的一种类型限定符(Type Qualifiers),用于告诉编译器,对象已经被指针所引用,不能通过除该指针外所有其他直接或间接的方式修改该对象的内容. 渊源: res ...
- PHP环境搭建时缺少php7apache2_4.dll怎么办
PHP环境搭建时缺少php7apache2_4.dll怎么办 下载的文件有问题! 1.在PHP官网点击Download下载时不管选择哪个版本的都有两个类型 如果需要 php7apache2_4.dl ...
- 【spring boot】idea下springboot打包成jar包和war包,并且可以在外部tomcat下运行访问到(转)
转自:https://www.cnblogs.com/sxdcgaq8080/p/7727249.html 接着上一章走呗:http://www.cnblogs.com/sxdcgaq8080/p ...
- git 撤回放到暂存区的文件
git reset HEAD filename 如:git reset HEAD test.txt 或者使用 git reset . 撤回所有文件(注意后面还有个.)
- Chapter 4 Invitations——5
And that was the last contact I'd had with him, though he was there, a foot away from me, every day. ...