jdk1.8.0_45源码解读——Map接口和AbstractMap抽象类的实现

一、 Map架构

如上图:
(01) Map 是映射接口,Map中存储的内容是键值对(key-value)
(02) AbstractMap 是继承于Map的抽象类,它实现了Map中的大部分API。其它Map的实现类可以通过继承AbstractMap来减少重复编码。
(03) SortedMap 是继承于Map的接口。SortedMap中的内容是排序的键值对,排序的方法是通过比较器(Comparator)。
(04) NavigableMap 是继承于SortedMap的接口。相比于SortedMap,NavigableMap有一系列的导航方法;如"获取大于/等于某对象的键值对"、“获取小于/等于某对象的键值对”等等。
(05) TreeMap 继承于AbstractMap,且实现了NavigableMap接口;因此,TreeMap中的内容是“有序的键值对”!
(06) HashMap 继承于AbstractMap,但没实现NavigableMap接口;因此,HashMap的内容是“键值对,但不保证次序”!
(07) Hashtable 虽然不是继承于AbstractMap,但它继承于Dictionary(Dictionary也是键值对的接口),而且也实现Map接口;因此,Hashtable的内容也是“键值对,也不保证次序”。但和HashMap相比,Hashtable是线程安全的,而且它支持通过Enumeration去遍历。
(08) WeakHashMap 继承于AbstractMap。它和HashMap的键类型不同,WeakHashMap的键是“弱键”

二、 Map接口

1. Map.Entry<K, V>的结构

  映射项(键-值对)。Map.entrySet() 方法返回映射的 collection 视图,其中的元素属于此类。获得映射项引用的唯一 方法是通过此 collection 视图的迭代器来实现。这些 Map.Entry 对象在迭代期间有效;更确切地讲,如果在迭代器返回项之后修改了底层映射,则某些映射项的行为是不确定的,除了通过 setValue 在映射项上执行操作之外。

K getKey();              //返回此项中对应的键值
V getValue();          //返回此项中对应的值
V setValue(V value);     //用指定的值替换与此项对应的值
boolean equals(Object o);   //比较指定对象与此项的相等性
int hashCode();         //返回此项的哈希码

2. Map接口

将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。

Map 接口提供三种collection 视图,允许以键集、值集或键-值映射关系集的形式查看某个映射的内容。映射顺序 定义为迭代器在映射的 collection 视图上返回其元素的顺序。某些映射实现可明确保证其顺序,如 TreeMap 类;另一些映射实现则不保证顺序,如 HashMap 类。

注:将可变对象用作映射键时必须格外小心。当对象是映射中某个键时,如果以影响 equals 比较的方式更改了对象的值,则映射的行为将是不确定的。此项禁止的一种特殊情况是不允许某个映射将自身作为一个键包含。虽然允许某个映射将自身作为值包含,但请格外小心:在这样的映射上 equalshashCode 方法的定义将不再是明确的。

public interface Map<K,V> {
int size();
boolean isEmpty();
boolean containsKey(Object key);
boolean containsValue(Object value);
V get(Object key);
V put(K key, V value);
V remove(Object key);
void putAll(Map<? extends K, ? extends V> m);
void clear();
Set<K> keySet();
Collection<V> values(); //返回此映射中包含的映射关系的 set 视图
Set<Map.Entry<K, V>> entrySet();
boolean equals(Object o);
int hashCode();
}

三、AbstractMap抽象类

1. AbstractMap.SimpleEntry<K, V>的结构

  SimpleEntry<K, V>是维护键和值的 Entry。可以使用 setValue() 方法更改值。此类简化了构建自定义映射实现的过程。例如,可以使用 Map.entrySet().toArray 方法方便地返回 SimpleEntry实例数组。

    public static class SimpleEntry<K,V>
implements Entry<K,V>, java.io.Serializable
{
private static final long serialVersionUID = -8499721149061103585L; private final K key;
private V value; //创建一个项,它表示从指定键到指定值的映射关系
public SimpleEntry(K key, V value) {
this.key = key;
this.value = value;
} //创建一个项,它表示的映射关系与指定的项相同
public SimpleEntry(Entry<? extends K, ? extends V> entry) {
this.key = entry.getKey();
this.value = entry.getValue();
} //返回对应于此项的键
public K getKey() {
return key;
} //返回对应于此项的值
public V getValue() {
return value;
} //用指定值替换对应于此项的值,并返回旧值
public V setValue(V value) {
V oldValue = this.value;
this.value = value;
return oldValue;
} //相等性检测,包括null值
private static boolean eq(Object o1, Object o2) {
return o1 == null ? o2 == null : o1.equals(o2);
} //比较指定对象与此项的相等性
public boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
return eq(key, e.getKey()) && eq(value, e.getValue());
} //返回此映射项的哈希值:key值的哈希码与value值的哈希码按位异或的结果
public int hashCode() {
return (key == null ? 0 : key.hashCode()) ^
(value == null ? 0 : value.hashCode());
} //返回此映射项的 String表示形式:key=value
public String toString() {
return key + "=" + value;
} }

2.AbstractMap.SimpleImmutableEntry<K,V>的结构

  SimpleImmutableEntry<K,V>是维护不可变的键和值的 Entry,不支持setValue()方法。方便用于在返回线程安全的键-值映射关系的方法。

    public static class SimpleImmutableEntry<K,V>
implements Entry<K,V>, java.io.Serializable
{
private static final long serialVersionUID = 7138329143949025153L; private final K key;
private final V value; //创建一个项,它表示从指定键到指定值的映射关系
public SimpleImmutableEntry(K key, V value) {
this.key = key;
this.value = value;
} //创建一个项,它表示的映射关系与指定项相同
public SimpleImmutableEntry(Entry<? extends K, ? extends V> entry) {
this.key = entry.getKey();
this.value = entry.getValue();
} //返回对应于此项的键
public K getKey() {
return key;
} //返回对应于此项的值
public V getValue() {
return value;
} //该方法仅抛出UnsupportedOperationException,表明该Entry不支持setValue方法
public V setValue(V value) {
throw new UnsupportedOperationException();
} //比较指定对象与此项的相等性
public boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
return eq(key, e.getKey()) && eq(value, e.getValue());
} //返回此映射项的哈希值:key值的哈希码与value值的哈希码按位异或的结果
public int hashCode() {
return (key == null ? 0 : key.hashCode()) ^
(value == null ? 0 : value.hashCode());
} //返回此映射项的 String表示形式:key=value
public String toString() {
return key + "=" + value;
}
}

3. AbstractMap抽象类

AbstractMap类提供 Map接口的骨干实现,以最大限度地减少实现此接口所需的工作。
要实现不可修改的映射,编程人员只需扩展此类并提供 entrySet() 方法的实现即可,该方法将返回映射的映射关系 set视图。通常,返回的 set
将依次在 AbstractSet 上实现。此 set不支持 add() 或 remove() 方法,其迭代器也不支持 remove()
方法。

要实现可修改的映射,编程人员必须另外重写此类的put() 方法(否则将抛出 UnsupportedOperationException),entrySet().iterator() 返回的迭代器也必须另外实现其 remove() 方法。

public abstract class AbstractMap<K,V> implements Map<K,V> {
//构造函数
protected AbstractMap() {
} //返回此映射中的键-值映射关系数
public int size() {
return entrySet().size();
} //如果此映射未包含键-值映射关系,则返回 true。
public boolean isEmpty() {
return size() == 0;
} //如果键值对<key,value>集合中包含指定的value值,则返回ture
public boolean containsValue(Object value) {
//entrySet()返回此映射中包含的映射关系的Set视图
Iterator<Entry<K,V>> i = entrySet().iterator();
//Map中允许value值为null,故分两种情况考虑
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;
} //如果键值对<key,value>集合中包含指定的key值,则返回ture
public boolean containsKey(Object key) {
//entrySet()返回此映射中包含的映射关系的Set视图
Iterator<Map.Entry<K,V>> i = entrySet().iterator();
//Map中只允许有唯一一个null的key值,故分两种情况考虑
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;
} //返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null
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;
} //将指定的值与此映射中的指定键关联
public V put(K key, V value) {
throw new UnsupportedOperationException();
} /**
* 如果存在一个键的映射关系,则将其从此映射中移除。并返回以前与key关联的值;如果没有key的映射关系,则返回null
*
* 注意,如果 entrySet()返回的集合所调用的迭代器不支持 remove方法,并且此映射包含指定键的映射关系,
* 则此实现将抛出 UnsupportedOperationException。
*/
public V remove(Object key) {
Iterator<Entry<K,V>> i = entrySet().iterator();
Entry<K,V> correctEntry = null; //用于指向给定键值对应的映射关系
//找到指定键的映射关系,并赋值给correctEntry
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(); //得到以前与key关联的值
i.remove(); //运用迭代器中的remove()方法来对得到的键值对项进行移除工作
}
return oldValue;
} //从指定Map中将所有映射关系<key,value>复制到此映射中
public void putAll(Map<? extends K, ? extends V> m) {
for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
put(e.getKey(), e.getValue());
} //从此映射中移除所有映射关系
public void clear() {
entrySet().clear();
} //用volatile修饰的变量,线程在每次使用变量的时候,都会读取变量最后修改后的值
transient volatile Set<K> keySet; //Map中key值的唯一性决定键值集合的视图类型为Set
transient volatile Collection<V> values; //Map中value值的可重复性决定值集合的视图类型为Collection //返回此映射中包含的键的 Set视图
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;
} //返回此映射中包含的值的 Collection视图
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(); //得到value值
} 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;
} //返回此映射中包含的映射关系的 Set 视图
public abstract Set<Entry<K,V>> entrySet(); //比较指定对象与此映射的相等性
public boolean equals(Object o) {
//如果给定对象也是一个映射并且两个映射表示相同的映射关系,则返回true
if (o == this)
return true;
//检查指定的对象是否为映射关系
if (!(o instanceof Map))
return false;
Map<?,?> m = (Map<?,?>) o; //强制类型转换
//检查指定的对象是否是一个大小与此映射相等的映射
if (m.size() != size())
return false;
//在此映射 entrySet()返回的collection上进行迭代,并检查指定的映射是否包含此映射所包含的每个映射关系
try {
Iterator<Entry<K,V>> i = entrySet().iterator();
while (i.hasNext()) {
Entry<K,V> e = i.next();
K key = e.getKey(); //此映射的key值
V value = e.getValue(); //此映射的value值
if (value == null) {
if (!(m.get(key)==null && m.containsKey(key)))
return false;
} else {
if (!value.equals(m.get(key)))
return false;
}
}
} catch (ClassCastException unused) {
return false;
} catch (NullPointerException unused) {
return false;
} return true;
} //返回此映射的哈希值
//Map的哈希码被定义为该映射的 entrySet()视图中每个条目的哈希码之和。这确保了 m1.equals(m2)对于任意两个映射m1和m2都意味着 m1.hashCode()==m2.hashCode()
public int hashCode() {
int h = 0;
Iterator<Entry<K,V>> i = entrySet().iterator();
while (i.hasNext())
h += i.next().hashCode();
return h;
} //返回此映射的字符串表示形式:{key1=value1}, {key1=value1}, ...
public String toString() {
Iterator<Entry<K,V>> i = entrySet().iterator();
if (! i.hasNext()) //Map为null
return "{}"; StringBuilder sb = new StringBuilder();
sb.append('{');
for (;;) {
Entry<K,V> e = i.next();
K key = e.getKey();
V value = e.getValue();
sb.append(key == this ? "(this Map)" : key);
sb.append('=');
sb.append(value == this ? "(this Map)" : value);
if (! i.hasNext())
return sb.append('}').toString();
sb.append(',').append(' ');
}
} //返回此 AbstractMap 实例的浅表副本:不复制键和值本身
protected Object clone() throws CloneNotSupportedException {
AbstractMap<?,?> result = (AbstractMap<?,?>)super.clone();
result.keySet = null;
result.values = null;
return result;
}
}

jdk1.8.0_45源码解读——Map接口和AbstractMap抽象类的实现的更多相关文章

  1. jdk1.8.0_45源码解读——Set接口和AbstractSet抽象类的实现

    jdk1.8.0_45源码解读——Set接口和AbstractSet抽象类的实现 一. Set架构 如上图: (01) Set 是继承于Collection的接口.它是一个不允许有重复元素的集合.(0 ...

  2. jdk1.8.0_45源码解读——HashSet的实现

    jdk1.8.0_45源码解读——HashSet的实现 一.HashSet概述 HashSet实现Set接口,由哈希表(实际上是一个HashMap实例)支持.主要具有以下的特点: 不保证set的迭代顺 ...

  3. jdk1.8.0_45源码解读——HashMap的实现

    jdk1.8.0_45源码解读——HashMap的实现 一.HashMap概述 HashMap是基于哈希表的Map接口实现的,此实现提供所有可选的映射操作.存储的是<key,value>对 ...

  4. jdk1.8.0_45源码解读——ArrayList的实现

    jdk1.8.0_45源码解读——ArrayList的实现 一.ArrayList概述 ArrayList是List接口的可变数组的实现.实现了所有可选列表操作,并允许包括 null 在内的所有元素. ...

  5. jdk1.8.0_45源码解读——LinkedList的实现

    jdk1.8.0_45源码解读——LinkedList的实现 一.LinkedList概述 LinkedList是List和Deque接口的双向链表的实现.实现了所有可选列表操作,并允许包括null值 ...

  6. JDK1.7.0_45源码阅读<java.lang.Boolean>

    本文适合的人群 其实感觉写这个标题的内容没有必要,只要你觉得对你有帮助那么就适合你,对你没帮助那么就不适合你.毕竟我不是专业作者,但咱会尽力的.其实最重要的一点是我不希望浪费您宝贵时间. 简要把内容在 ...

  7. JDK容器类Map源码解读

    java.util.Map接口是JDK1.2开始提供的一个基于键值对的散列表接口,其设计的初衷是为了替换JDK1.0中的java.util.Dictionary抽象类.Dictionary是JDK最初 ...

  8. 【JDK1.8】JDK1.8集合源码阅读——HashMap

    一.前言 笔者之前看过一篇关于jdk1.8的HashMap源码分析,作者对里面的解读很到位,将代码里关键的地方都说了一遍,值得推荐.笔者也会顺着他的顺序来阅读一遍,除了基础的方法外,添加了其他补充内容 ...

  9. 源码解读—HashTable

    在上一篇学习过HashMap(源码解读—HashMap)之后对hashTable也产生了兴趣,随即便把hashTable的源码看了一下.和hashMap类似,但是也有不同之处. public clas ...

随机推荐

  1. Json和Map互转,四个包(org.json/net.sf.json/com.google.gson/com.alibaba.fastjson)

    目前使用的(org.json/net.sf.json/com.google.gson/com.alibaba.fastjson)这四种json-map互转,其他的以后在补充.............. ...

  2. Python数据信号处理库RadioDSP: 引入ThinkDSP实现思想

    RadioDSP是针对无线通信领域的数字信号处理库,它采用了ThinkDSP的思想,对于无线通信中的IQ信号可以绘制频谱图和时域图.目前项目还在起始阶段,详细的代码可以参考链接: https://gi ...

  3. k8s-rabbitmq-(一)集群部署

    K8S版本:1.10.1 rabbitmq版本:3.6.14 从来没用过这个软件,所以对里面很多术语看不太懂.最后通过https://www.kubernetes.org.cn/2629.html 大 ...

  4. 11.7 Daily Scrum(周末暂停两天Daily Scrum)

    由于APEC放假,有些成员离校了,他们那部分的任务会暂时拖后一些,之后会加班加点赶工. 另外,每个人的任务还是相对独立,离校成员的任务进度不会对其他成员的进度造成很大影响.   Today's tas ...

  5. 决胜 Poker

    团队展示 队名 决胜 Poker 团队人员 211606392 郑俊瑜 (队长) 211606355 陈映宏 211606358 陈卓楠 211606386 姚皓钰 211606323 刘世华 211 ...

  6. Jquery封装ajax

    Jquery封装ajax   Load方法     <!-- 将jquery.js导入进来 -->     <script type="text/javascript&qu ...

  7. Beta版本讨论

    目录 组员:胡绪佩 组员:何家伟 组员:黄鸿杰 组员: 翟丹丹 组员:周政演 组员:胡青元 组员:庄卉 组员:刘恺琳 组员:何宇恒 组员:刘一好 组员:葛家灿 组员:胡绪佩 总结 通过这次的Beta版 ...

  8. 『编程题全队』alpha阶段项目复审

    小组的名字和链接 优点 缺点,bug 报告 最终名次 Gakki赛高 (1)支持注册账号和账号管理(2) 支持自动登录,提供便捷性(3)题目不重复且题目答案准确(4)支持排行榜统计功能(5)自己设计算 ...

  9. beta 圆桌桌 4

    031602111 傅海涛 1.今天进展 后台接口大部分完善,并完成交互 2.存在问题 部分接口不稳定 3.明天安排 完成全部接口的交互 4.心得体会 小问题真多,要一个一个解决 031602115 ...

  10. PAT 甲级 1096 Consecutive Factors

    https://pintia.cn/problem-sets/994805342720868352/problems/994805370650738688 Among all the factors ...