引言

今天学习一个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的更多相关文章

  1. Java集合类源码解析:Vector

    [学习笔记]转载 Java集合类源码解析:Vector   引言 之前的文章我们学习了一个集合类 ArrayList,今天讲它的一个兄弟 Vector.为什么说是它兄弟呢?因为从容器的构造来说,Vec ...

  2. Java集合类源码解析:HashMap (基于JDK1.8)

    目录 前言 HashMap的数据结构 深入源码 两个参数 成员变量 四个构造方法 插入数据的方法:put() 哈希函数:hash() 动态扩容:resize() 节点树化.红黑树的拆分 节点树化 红黑 ...

  3. Java集合类源码解析:ArrayList

    目录 前言 源码解析 基本成员变量 添加元素 查询元素 修改元素 删除元素 为什么用 "transient" 修饰数组变量 总结 前言 今天学习一个Java集合类使用最多的类 Ar ...

  4. Java集合类源码解析:LinkedHashMap

    前言 今天继续学习关于Map家族的另一个类 LinkedHashMap .先说明一下,LinkedHashMap 是继承于 HashMap 的,所以本文只针对 LinkedHashMap 的特性学习, ...

  5. Java集合类源码解析:AbstractList

    今天学习Java集合类中的一个抽象类,AbstractList. 初识AbstractList AbstractList 是一个抽象类,实现了List<E>接口,是隶属于Java集合框架中 ...

  6. 【转】Java HashMap 源码解析(好文章)

    ­ .fluid-width-video-wrapper { width: 100%; position: relative; padding: 0; } .fluid-width-video-wra ...

  7. JDK8集合类源码解析 - HashSet

    HashSet 特点:不允许放入重复元素 查看源码,发现HashSet是基于HashMap来实现的,对HashMap做了一次“封装”. private transient HashMap<E,O ...

  8. Java——LinkedHashMap源码解析

    以下针对JDK 1.8版本中的LinkedHashMap进行分析. 对于HashMap的源码解析,可阅读Java--HashMap源码解析 概述   哈希表和链表基于Map接口的实现,其具有可预测的迭 ...

  9. Java - TreeMap源码解析 + 红黑树

    Java提高篇(二七)-----TreeMap TreeMap的实现是红黑树算法的实现,所以要了解TreeMap就必须对红黑树有一定的了解,其实这篇博文的名字叫做:根据红黑树的算法来分析TreeMap ...

随机推荐

  1. day18_雷神_django第一天

    # django_day01 1.http 协议 超文本传输协议,HTTP有很多应用,但最著名的是用于web浏览器和web服务器之间的双工通信. 协议概述 HTTP是一个客户端终端(用户)和服务器端( ...

  2. 复习java基础

    十进制转换成二进制:     方法:整除法,计数方式从右往左,二进制中非0即1.例子如下: 计数方式是从右往左进行,然后填写数字的顺序是余数优先 二进制转换成十进制: 方法:乘二法,例如二进制数字为: ...

  3. 当GDPR来敲门,中国互联网企业该如何应对?

    本文来自 网易云社区 . 欧盟<通用数据保护条例>(General Data Protection Regulation,GDPR)已于2018年5月25日正式生效,谷歌.Facebook ...

  4. 【分布式缓存系列】Redis实现分布式锁的正确姿势

    一.前言 在我们日常工作中,除了Spring和Mybatis外,用到最多无外乎分布式缓存框架——Redis.但是很多工作很多年的朋友对Redis还处于一个最基础的使用和认识.所以我就像把自己对分布式缓 ...

  5. 我所理解的HTTP协议

    前言 对于HTTP协议,想必大家都不陌生,在工作中经常用到,特别是针对移动端和前端开发人员来说,要获取服务端数据,基本走的网络请求都是基于HTTP协议,特别是RESTFUL + JSON 这种搭配特别 ...

  6. 【SP26073】DIVCNT1 - Counting Divisors 题解

    题目描述 定义 \(d(n)\) 为 \(n\) 的正因数的个数,比如 \(d(2) = 2, d(6) = 4\). 令 $ S_1(n) = \sum_{i=1}^n d(i) $ 给定 \(n\ ...

  7. Android MediaPlayer SeekTo 在 8.0 版本上优化说明

    android使用 mediaPlayer 播放video视频过程中, 当用户退出当前播放,再从后台恢复播放时,需要跳转到之前退出的时间点继续播放.使用的方法基本都是 SeekTo 之前的时间点,但是 ...

  8. 第44节:Java当中的JVM

    Java当中JVM 01 在使用控制面板时的实质: Hello.java使用javac,然后变成为Hello.class通过运行java这个命令,在类加载器中(含有加载,验证,准备,解析,初始化,使用 ...

  9. 微信小程序支付接入实战

    1. 微信小程序支付接入实战 1.1. 需求   最近接到一个小程序微信支付的需求,需要我写后台支持,本着能不自己写就不自己写的cv原则,在网上找到了些第三方程序,经过尝试后,最后决定了这不要脸作者的 ...

  10. feign包名路径添加问题

    1. feign包名路径添加问题 1.1. 问题 在SpringCloud中使用feign调用路径中,不能在类上直接添加@RequestMapping(value = "/hospital- ...