路径java.util.AbstractMap

()构造方法

  1. /**
  2. * 唯一的构造器。(一般由子类隐式调用)
  3. */
  4. protexted AbstractMap(){
  5. }

size()返回当前map的大小

  1. public int size() {
  2. return entrySet().size();
  3. }

这里的entrySet()返回一个Set<Entry<K,V>>对象。但是当前类AbstractMap没有实现它。下同

isEmpty()判断当前Map是否为空

  1. public boolean isEmpty() {
  2. return size() == 0;
  3. }

containsKey(Object key)是否包含指定key

  1. public boolean containsKey(Object key) {
  2. Iterator<Map.Entry<K,V>> i = entrySet().iterator();
  3. if (key==null) {
  4. while (i.hasNext()) {
  5. Entry<K,V> e = i.next();
  6. if (e.getKey()==null)
  7. return true;
  8. }
  9. } else {
  10. while (i.hasNext()) {
  11. Entry<K,V> e = i.next();
  12. if (key.equals(e.getKey()))
  13. return true;
  14. }
  15. }
  16. return false;
  17. }

同样依靠entrySet()方法,使用迭代器检查每一个EntryKey

当参数key为空时,有任何一个Entrykey值为空则返回true

当参数key不为空时,参数keyequals方法与任何一个key返回true时本方法返回true

其他情况返回false

get(Object key)获取指定val

  1. public V get(Object key) {
  2. Iterator<Entry<K,V>> i = entrySet().iterator();
  3. if (key==null) {
  4. while (i.hasNext()) {
  5. Entry<K,V> e = i.next();
  6. if (e.getKey()==null)
  7. return e.getValue();
  8. }
  9. } else {
  10. while (i.hasNext()) {
  11. Entry<K,V> e = i.next();
  12. if (key.equals(e.getKey()))
  13. return e.getValue();
  14. }
  15. }
  16. return null;
  17. }

containsKey(Object key)相同,返回值由bool变成了EntrygetVale的返回值

其他情况下,返回null

put(K key, V value)添加一个键值对

  1. public V put(K key, V value) {
  2. throw new UnsupportedOperationException();
  3. }

直接抛出异常UnsupportedOperationException

remove(Object key)删除指定键值

  1. public V remove(Object key) {
  2. Iterator<Entry<K,V>> i = entrySet().iterator();
  3. Entry<K,V> correctEntry = null;
  4. if (key==null) {
  5. while (correctEntry==null && i.hasNext()) {
  6. Entry<K,V> e = i.next();
  7. if (e.getKey()==null)
  8. correctEntry = e;
  9. }
  10. } else {
  11. while (correctEntry==null && i.hasNext()) {
  12. Entry<K,V> e = i.next();
  13. if (key.equals(e.getKey()))
  14. correctEntry = e;
  15. }
  16. }
  17. V oldValue = null;
  18. if (correctEntry !=null) {
  19. oldValue = correctEntry.getValue();
  20. i.remove();
  21. }
  22. return oldValue;
  23. }

基于entrySet(),获取到对应Entry后,缓存其val,并在迭代器中删除找到的Entry,然后返回val

putAll(Map<? extends K, ? extends V> m)添加指定Map中的键值对到当前当前Map

  1. public void putAll(Map<? extends K, ? extends V> m) {
  2. for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
  3. put(e.getKey(), e.getValue());
  4. }

基于entrySet(),迭代调用put(K key, V value)方法进行操作

本类中put(K key, V value)的实现为直接抛出UnsupportedOperationException()异常

clear() 清空Map

  1. public void clear() {
  2. entrySet().clear();
  3. }

直接清空entrySet()所返回的Set集合

视图keySetvalues

  1. transient Set<K> keySet;
  2. transient Collection<V> values;

这两个变量主要用于keySet()values()方法。

Set<K> keySet()获取key集合

  1. public Set<K> keySet() {
  2. Set<K> ks = keySet;
  3. if (ks == null) {
  4. ks = new AbstractSet<K>() {
  5. public Iterator<K> iterator() {
  6. return new Iterator<K>() {
  7. private Iterator<Entry<K,V>> i = entrySet().iterator();
  8. public boolean hasNext() {
  9. return i.hasNext();
  10. }
  11. public K next() {
  12. return i.next().getKey();
  13. }
  14. public void remove() {
  15. i.remove();
  16. }
  17. };
  18. }
  19. public int size() {
  20. return AbstractMap.this.size();
  21. }
  22. public boolean isEmpty() {
  23. return AbstractMap.this.isEmpty();
  24. }
  25. public void clear() {
  26. AbstractMap.this.clear();
  27. }
  28. public boolean contains(Object k) {
  29. return AbstractMap.this.containsKey(k);
  30. }
  31. };
  32. keySet = ks;
  33. }
  34. return ks;
  35. }

此方法会初始化成员变量keySet并保持它的单例(因为没有做同步处理,所以有可能在并发环境下返回不同的对象)。

此方法构造的Set集合实际类型为AbstractSet的匿名内部类,主要有如下实现

  1. iterator()方法的实现每次构造一个新的Iterator对象,并在内部保存外部类的entrySet()iterator()方法所返回的迭代器对象。作为委派目标i
  2. 新的Iterator对象的hasNext(), next(),remove()方法均委托到变量i
  3. AbstractSet其他的实现方法size(), isEmpty(), clear(),contains(Object k)全部委托到外部类AbstractMap的同名方法

这里发生了一次数据上的可能的分离,就是iterator()所返回对象内部对象i来自entrySet().iterator(),而此时其他的方法如size()使用的实际方法为entrySet().size(),有可能会发生数据不同步的情况

values() 获取值集合

  1. public Collection<V> values() {
  2. Collection<V> vals = values;
  3. if (vals == null) {
  4. vals = new AbstractCollection<V>() {
  5. public Iterator<V> iterator() {
  6. return new Iterator<V>() {
  7. private Iterator<Entry<K,V>> i = entrySet().iterator();
  8. public boolean hasNext() {
  9. return i.hasNext();
  10. }
  11. public V next() {
  12. return i.next().getValue();
  13. }
  14. public void remove() {
  15. i.remove();
  16. }
  17. };
  18. }
  19. public int size() {
  20. return AbstractMap.this.size();
  21. }
  22. public boolean isEmpty() {
  23. return AbstractMap.this.isEmpty();
  24. }
  25. public void clear() {
  26. AbstractMap.this.clear();
  27. }
  28. public boolean contains(Object v) {
  29. return AbstractMap.this.containsValue(v);
  30. }
  31. };
  32. values = vals;
  33. }
  34. return vals;
  35. }

keySet()相同,只是返回类型换为允许重复元素AbstractCollection

equals(Object o)比较两个Map是否相同

  1. public boolean equals(Object o) {
  2. if (o == this)
  3. return true;
  4. if (!(o instanceof Map))
  5. return false;
  6. Map<?,?> m = (Map<?,?>) o;
  7. if (m.size() != size())
  8. return false;
  9. try {
  10. Iterator<Entry<K,V>> i = entrySet().iterator();
  11. while (i.hasNext()) {
  12. Entry<K,V> e = i.next();
  13. K key = e.getKey();
  14. V value = e.getValue();
  15. if (value == null) {
  16. if (!(m.get(key)==null && m.containsKey(key)))
  17. return false;
  18. } else {
  19. if (!value.equals(m.get(key)))
  20. return false;
  21. }
  22. }
  23. } catch (ClassCastException unused) {
  24. return false;
  25. } catch (NullPointerException unused) {
  26. return false;
  27. }
  28. return true;
  29. }

比较流程如下

  1. 如果是同一个对象,则返回true
  2. 如果入参不是Map的子类,直接返回false
  3. 如果两者的size()返回的数量不同,直接返回false
  4. 使用entrySet().iterator()获取当前对象的迭代器并进行迭代。进行如下操作
  5. 迭代中。当val为空时,使用key向入参map进行值获取,结果值不为空或不包含这个key时,返回false
  6. 迭代中。当val为不空时,使用key向入参map进行值获取,当使用equals比较两者不相同时,返回false
  7. 迭代中出现ClassCastExceptionNullPointerException返回false
  8. 执行到结尾,返回true

hashCode()获取HashCode

  1. public int hashCode() {
  2. int h = 0;
  3. Iterator<Entry<K,V>> i = entrySet().iterator();
  4. while (i.hasNext())
  5. h += i.next().hashCode();
  6. return h;
  7. }

迭代所有Entry,累加所有EntryHashCode

clone()clone当前对象

  1. protected Object clone() throws CloneNotSupportedException {
  2. AbstractMap<?,?> result = (AbstractMap<?,?>)super.clone();
  3. result.keySet = null;
  4. result.values = null;
  5. return result;
  6. }

注意,这里是浅拷贝,并对新对象的keySetvalues属性进行置空

由于当前抽象类的绝大多数实现是基于方法entrySet()方法,所以这个方法需要由实现类进行关注。防止浅拷贝后,新对象指向老引用引发问题

Java源码记录 - AbstractMap的更多相关文章

  1. Java源码解读(一)——HashMap

    HashMap作为常用的一种数据结构,阅读源码去了解其底层的实现是十分有必要的.在这里也分享自己阅读源码遇到的困难以及自己的思考. HashMap的源码介绍已经有许许多多的博客,这里只记录了一些我看源 ...

  2. 【java集合框架源码剖析系列】java源码剖析之java集合中的折半插入排序算法

    注:关于排序算法,博主写过[数据结构排序算法系列]数据结构八大排序算法,基本上把所有的排序算法都详细的讲解过,而之所以单独将java集合中的排序算法拿出来讲解,是因为在阿里巴巴内推面试的时候面试官问过 ...

  3. JVM之---Java源码编译机制

    Sun JDK中采用javac将Java源码编译为class文件,这个过程包含三个步骤:     1.分析和输入到符号表(Parse and Enter)    Parse过程所做的工作有词法和语法分 ...

  4. 从Java源码到Java字节码

    Java最主流的源码编译器,javac,基本上不对代码做优化,只会做少量由Java语言规范要求或推荐的优化:也不做任何混淆,包括名字混淆或控制流混淆这些都不做.这使得javac生成的代码能很好的维持与 ...

  5. 【JDK命令行 一】手动编译Java源码与执行字节码命令合集(含外部依赖引用)

    写作目标 记录常见的使用javac手动编译Java源码和java手动执行字节码的命令,一方面用于应对 Maven 和 Gradle 暂时无法使用的情况,临时生成class文件(使用自己的jar包):另 ...

  6. 如何阅读Java源码 阅读java的真实体会

    刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动. 源码阅读,我觉得最核心有三点:技术基础+强烈的求知欲+耐心.   说到技术基础,我打个比 ...

  7. Android反编译(一)之反编译JAVA源码

    Android反编译(一) 之反编译JAVA源码 [目录] 1.工具 2.反编译步骤 3.实例 4.装X技巧 1.工具 1).dex反编译JAR工具  dex2jar   http://code.go ...

  8. 如何阅读Java源码

    刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动.源码阅读,我觉得最核心有三点:技术基础+强烈的求知欲+耐心. 说到技术基础,我打个比方吧, ...

  9. Java 源码学习线路————_先JDK工具包集合_再core包,也就是String、StringBuffer等_Java IO类库

    http://www.iteye.com/topic/1113732 原则网址 Java源码初接触 如果你进行过一年左右的开发,喜欢用eclipse的debug功能.好了,你现在就有阅读源码的技术基础 ...

随机推荐

  1. 使用Python开发小说下载器,不再为下载小说而发愁 #华为云·寻找黑马程序员#

    需求分析 免费的小说网比较多,我看的比较多的是笔趣阁.这个网站基本收费的章节刚更新,它就能同步更新,简直不要太叼.既然要批量下载小说,肯定要分析这个网站了- 在搜索栏输入地址后,发送post请求获取数 ...

  2. Json schema 以及在python中的jsonschema

    目录 1. JSON Schema简介 2. JSON Schema关键字详解 2.1 $schema 2.2 title和description 2.3 type 3 type常见取值 3.1 当t ...

  3. JS中的运算符_函数学习

    js中的运算符:     算数运算符:     + - * / % ++ --          逻辑运算符:     & | !  && || < > <= ...

  4. CodeForces1000A-Light It Up

    B. Light It Up time limit per test 1 second memory limit per test 256 megabytes input standard input ...

  5. AcWing 291.蒙德里安的梦想

    题目:蒙德里安的梦想 链接:(蒙德里安的梦想)[https://www.acwing.com/problem/content/293/] 题意:求把N * M的棋盘分割成若干个1 * 2的长方形,有多 ...

  6. Python3 猜年龄小游戏进阶之函数处理

    在猜年龄的基础上编写登录.注册方法,并且把猜年龄游戏分函数处理 登录函数 注册函数 猜年龄函数 选择奖品函数 # 注册 def register(): '''注册''' count = 0 while ...

  7. Kubernetes 应用部署实战

    Kubernetes 应用部署实战 2018-08-08 19:44:56 wuxiangping2017 阅读数 3084  收藏 更多 分类专栏: linux运维与架构师   简介 伙计们,请搬好 ...

  8. 【Java Web开发学习】跨域请求

    [Java Web开发学习]跨域请求 ================================================= 1.使用jsonp ===================== ...

  9. Golang 入门系列(十七)几个常见的并发模型——生产者消费者模型

    前面已经讲过很多Golang系列知识,包括并发,锁等内容,感兴趣的可以看看以前的文章,https://www.cnblogs.com/zhangweizhong/category/1275863.ht ...

  10. CSS | 圣杯布局、双飞翼布局 | 自适应三栏布局

    圣杯布局和双飞翼布局是前端工程师需要日常掌握的重要布局方式.两者的功能相同,都是为了实现一个两侧宽度固定,中间宽度自适应的三栏布局 虽然两者的实现方法略有差异,不过都遵循了以下要点: 1.两侧宽度固定 ...