引言

今天学习一个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. day_7数据类型的相互转换,与字符编码

    首先复一下昨天的内容 1:深浅拷贝 1:值拷贝 直接赋值 列表1=列表2       列表1中的任何值发生改变,列表2中的值都会随之改变 2:浅拷贝,列表2=列表1  列表1中存放的值的地址没有改变, ...

  2. Oracle数据库用EF操作的示例

    Using EF Oracle Sample Provider with EDM Designer  (from msdn) Many people are asking if it is possi ...

  3. Dubbo 源码分析 - 集群容错之 LoadBalance

    1.简介 LoadBalance 中文意思为负载均衡,它的职责是将网络请求,或者其他形式的负载"均摊"到不同的机器上.避免集群中部分服务器压力过大,而另一些服务器比较空闲的情况.通 ...

  4. JAVA泛型实现原理

    1. Java范型时编译时技术,在运行时不包含范型信息,仅仅Class的实例中包含了类型参数的定义信息.泛型是通过java编译器的称为擦除(erasure)的前端处理来实现的.你可以(基本上就是)把它 ...

  5. 315道python面试题(参考答案)

    第一部分 Python基础篇 1:为什么学习Python 家里有在这个IT圈子里面,也想让我接触这个圈子,然后给我建议学的Python, 然后自己通过百度和向有学过Python的同学了解了Python ...

  6. HBase数据持久化之HRegion.flushcache即CF持久化

    我们从HRegion.flushcache开始分析.由于这个过程还是比较复杂的,因此,在这一节,我们不会分析的太过详细,只是单单的介绍一下其大体流程,待大家有了整体的把握之后,我们将在下一节详细介绍其 ...

  7. CSS box-sizing属性

    全文摘抄自 https://developer.mozilla.org/zh-CN/docs/Web/CSS/box-sizing 专门抄一遍是因为,我想当然的以为标准盒子模型设置的宽高是包括padd ...

  8. 解决删除chrome注册表残留问题

    将下面这个全部复制下来并粘贴到命名为“remove.reg”的文件中.双击执行即可  Windows Registry Editor Version 5.00 ;WARNING, this file  ...

  9. 通过Microsoft Learn进行学习以提升技能

    通过 Microsoft Learn,可以免费而且轻松有趣地学习 Microsoft 技术. Microsoft Learn的与众不同 借助 Microsoft Learn,任何人都能按自己的学习计划 ...

  10. sql server 索引阐述系列五 索引参数与碎片

    -- 创建聚集索引 create table [dbo].[pub_stocktest] add constraint [pk_pub_stocktest] primary key clustered ...