一、前言

  在遍历HashMap与LinkedHashMap时,我们通常都会使用到迭代器,而HashMap的迭代器与LinkedHashMap迭代器是如何工作的呢?下面我们来一起分析分析。

二、迭代器继承图

  

  

三、HashMap迭代器

  3.1 HashIterator

  HashIterator是一个抽象类,封装了迭代器内部工作的一些操作。

  HashIterator类属性

abstract class HashIterator {
// 下一个结点
Node<K,V> next; // next entry to return
// 当前结点
Node<K,V> current; // current entry
// 期望的修改次数
int expectedModCount; // for fast-fail
// 当前桶索引
int index; // current slot
}

  说明:其中expectedModCount属性主要用于在遍历HashMap同时,程序对其结构是否进行了修改。若遍历同时修改了,则会抛出异常。

  HashIterator构造函数 

HashIterator() {
// 成员变量赋值
expectedModCount = modCount;
Node<K,V>[] t = table;
current = next = null;
index = 0;
// table不为空并且大小大于0
if (t != null && size > 0) { // advance to first entry
// 找到table数组中第一个存在的结点,即找到第一个具有元素的桶
do {} while (index < t.length && (next = t[index++]) == null);
}
}

  说明:next将表示第一个非空桶中的第一个结点,index将表示下一个桶。

  HashIterator核心函数分析

  1. hasNext函数

// 是否存在下一个结点
public final boolean hasNext() {
return next != null;
}

  2. nextNode函数 

final Node<K,V> nextNode() {
// 记录next结点
Node<K,V> e = next;
// 若在遍历时对HashMap进行结构性的修改则会抛出异常
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
// 下一个结点为空,抛出异常
if (e == null)
throw new NoSuchElementException();
// 如果下一个结点为空,并且table表不为空;表示桶中所有结点已经遍历完,需寻找下一个不为空的桶
if ((next = (current = e).next) == null && (t = table) != null) {
// 找到下一个不为空的桶
do {} while (index < t.length && (next = t[index++]) == null);
}
return e;
}

  说明:nextNode函数屏蔽掉了桶的不同所带来的差异,就好像所有元素在同一个桶中,依次进行遍历。

  3. remove函数

public final void remove() {
Node<K,V> p = current;
// 当前结点为空,抛出异常
if (p == null)
throw new IllegalStateException();
// 若在遍历时对HashMap进行结构性的修改则会抛出异常
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
// 当前结点为空
current = null;
K key = p.key;
// 移除结点
removeNode(hash(key), key, null, false, false);
// 赋最新值
expectedModCount = modCount;
}

  3.2 KeyIterator

  KeyIterator类是键迭代器,继承自HashIterator,实现了Iterator接口,可以对HashMap中的键进行遍历。

  类定义 

final class KeyIterator extends HashIterator
implements Iterator<K> {
public final K next() { return nextNode().key; }
}

  3.3 ValueIterator

  ValueIterator类是值迭代器,继承自HashIterator,实现了Iterator接口,与KeyIterator类似,对值进行遍历。  

final class ValueIterator extends HashIterator
implements Iterator<V> {
public final V next() { return nextNode().value; }
}

  3.4 EntryIterator

  EntryIterator类是结点迭代器,继承自HashIterator,实现了Iterator接口,与KeyIterator、ValueIterator类似,对结点进行遍历。 

final class ValueIterator extends HashIterator
implements Iterator<V> {
public final V next() { return nextNode().value; }
}

四、LinkedHashMap迭代器

  4.1 LinkedHashIterator

  LinkedHashIterator是LinkedHashMap的迭代器,为抽象类,用于对LinkedHashMap进行迭代。 

  LinkedHashIterator类属性

abstract class LinkedHashIterator {
// 下一个结点
LinkedHashMap.Entry<K,V> next;
// 当前结点
LinkedHashMap.Entry<K,V> current;
// 期望的修改次数
int expectedModCount;
}

  LinkedHashIterator构造函数  

LinkedHashIterator() {
// next赋值为头结点
next = head;
// 赋值修改次数
expectedModCount = modCount;
// 当前结点赋值为空
current = null;
}

  LinkedHashIterator核心函数

  hasNext函数

// 是否存在下一个结点
public final boolean hasNext() {
return next != null;
}

  nextNode函数 

final LinkedHashMap.Entry<K,V> nextNode() {
LinkedHashMap.Entry<K,V> e = next;
// 检查是否存在结构性修改
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
// 当前结点是否为空
if (e == null)
throw new NoSuchElementException();
// 赋值当前节点
current = e;
// 赋值下一个结点
next = e.after;
return e;
}

  说明:由于所有的结点构成双链表结构,所以nextNode函数也很好理解,直接取得下一个结点即可。

public final void remove() {
// 保存当前结点
Node<K,V> p = current;
if (p == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
current = null;
K key = p.key;
// 移除结点
removeNode(hash(key), key, null, false, false);
// 更新最新修改数
expectedModCount = modCount;
}

  4.2 LinkedKeyIterator

  LinkedHashMap的键迭代器,继承自LinkedHashIterator,实现了Iterator接口,对LinkedHashMap中的键进行迭代。 

final class LinkedKeyIterator extends LinkedHashIterator
implements Iterator<K> {
public final K next() { return nextNode().getKey(); }
}

  4.3 LinkedValueIterator

  LinkedHashMap的值迭代器,继承自LinkedHashIterator,实现了Iterator接口,对LinkedHashMap中的值进行迭代。

final class LinkedValueIterator extends LinkedHashIterator
implements Iterator<V> {
public final V next() { return nextNode().value; }
}

  4.4 LinkedEntryIterator

  LinkedHashMap的结点迭代器,继承自LinkedHashIterator,实现了Iterator接口,对LinkedHashMap中的结点进行迭代。 

final class LinkedEntryIterator extends LinkedHashIterator
implements Iterator<Map.Entry<K,V>> {
public final Map.Entry<K,V> next() { return nextNode(); }
}

五、总结

  HashMap迭代器与LinkedHashMap迭代器有很多相似的地方,对比进行学习效果更佳。迭代器要屏蔽掉底层的细节,提供统一的接口供用户访问。HashMap与LinkedHashMap的迭代器源码分析就到此为止,还是很简单的,谢谢各位园友观看~

【集合框架】JDK1.8源码分析之HashMap & LinkedHashMap迭代器(三)的更多相关文章

  1. 【集合框架】JDK1.8源码分析之HashMap(一) 转载

    [集合框架]JDK1.8源码分析之HashMap(一)   一.前言 在分析jdk1.8后的HashMap源码时,发现网上好多分析都是基于之前的jdk,而Java8的HashMap对之前做了较大的优化 ...

  2. 【集合框架】JDK1.8源码分析之HashMap(一)

    一.前言 在分析jdk1.8后的HashMap源码时,发现网上好多分析都是基于之前的jdk,而Java8的HashMap对之前做了较大的优化,其中最重要的一个优化就是桶中的元素不再唯一按照链表组合,也 ...

  3. 【集合框架】JDK1.8源码分析之HashMap

    一.前言 在分析jdk1.8后的HashMap源码时,发现网上好多分析都是基于之前的jdk,而Java8的HashMap对之前做了较大的优化,其中最重要的一个优化就是桶中的元素不再唯一按照链表组合,也 ...

  4. JDK1.8源码分析之HashMap(一) (转)

    一.前言 在分析jdk1.8后的HashMap源码时,发现网上好多分析都是基于之前的jdk,而Java8的HashMap对之前做了较大的优化,其中最重要的一个优化就是桶中的元素不再唯一按照链表组合,也 ...

  5. JDK1.8源码分析之HashMap

    一.前言 在分析jdk1.8后的HashMap源码时,发现网上好多分析都是基于之前的jdk,而Java8的HashMap对之前做了较大的优化,其中最重要的一个优化就是桶中的元素不再唯一按照链表组合,也 ...

  6. 【集合框架】JDK1.8源码分析HashSet && LinkedHashSet(八)

    一.前言 分析完了List的两个主要类之后,我们来分析Set接口下的类,HashSet和LinkedHashSet,其实,在分析完HashMap与LinkedHashMap之后,再来分析HashSet ...

  7. 【集合框架】JDK1.8源码分析之ArrayList详解(一)

    [集合框架]JDK1.8源码分析之ArrayList详解(一) 一. 从ArrayList字表面推测 ArrayList类的命名是由Array和List单词组合而成,Array的中文意思是数组,Lis ...

  8. 【集合框架】JDK1.8源码分析之Collections && Arrays(十)

    一.前言 整个集合框架的常用类我们已经分析完成了,但是还有两个工具类我们还没有进行分析.可以说,这两个工具类对于我们操作集合时相当有用,下面进行分析. 二.Collections源码分析 2.1 类的 ...

  9. 集合之TreeSet(含JDK1.8源码分析)

    一.前言 前面分析了Set接口下的hashSet和linkedHashSet,下面接着来看treeSet,treeSet的底层实现是基于treeMap的. 四个关注点在treeSet上的答案 二.tr ...

随机推荐

  1. SQL Server2008附加数据库之后显示为只读时解决方法

    啰嗦的话就不多说了,直入主题吧! 方案一: 碰到这中情况一般是使用的sa账户登录的,只要改为Windows身份验证,再附加数据库即可搞定. 方案二: 使用sa登录SQL Server2008附加数据库 ...

  2. for循环中的 else,break

  3. java多态性,父类引用指向子类对象

    父类引用指向子类对象指的是: 例如父类Animal,子类Cat,Dog.其中Animal可以是类也可以是接口,Cat和Dog是继承或实现Animal的子类. Animal animal = new C ...

  4. Mac OS X上IntelliJ IDEA 13与Tomcat 8的Java Web开发环境搭建

    这标题实在有点拗口,不知道怎么写好,但看了标题也就明白文本的内容.最近几天在折腾这些玩意儿,所以写写总结.除了环境搭建,本文还是一篇入门级的上手教程. 去下载一些东西 JDK安装 Tomcat安装 T ...

  5. MyISAM和InnoDB

    MyISAM和InnoDB MyISAM MyISAM使用B+tree作为索引结构,叶节点存放的是数据地址. MyISAM不支持事务和外键. MyISAM是表锁,对数据库写操作时会锁住整个表,效率低. ...

  6. 深入挖掘.NET序列化机制——实现更易用的序列化方案

    .NET框架为程序员提供了“序列化和反序列化”这一有力的工具,使用它,我们能很容易的将内存中的对象图转化为字节流,并在需要的时候再将其恢复.这一技术的典型应用场景包括[1] : 应用程序运行状态的持久 ...

  7. 你真的会用java replaceAll函数吗?

    replace.replaceAll.replaceFirst这三个函数会java的同学估计都用过,笔者已经用了2年多,可是,我们真的懂他们吗? 概述一下他们三个的用法: · replace(Char ...

  8. iOS开发-应用崩溃日志揭秘(一)

    作为一名应用开发者,你是否有过如下经历? 为确保你的应用正确无误,在将其提交到应用商店之前,你必定进行了大量的测试工作.它在你的设备上也运行得很好,但是,上了应用商店后,还是有用户抱怨会闪退 ! 如果 ...

  9. 有关binlog的那点事(二)(mysql5.7.13)

    上次,我们仅仅把binlog做了一个概述,并没有去深入探索(1)binlog file究竟是怎么构成的?(2)binlog file的单元binlog events是怎么构成的?(3)我们能不能伪造出 ...

  10. ECS Win2008 远程时提示"要登录到此远程计算机,您必须被授予允许通过终端登录登录的权限"的解决方法

    问题描述 ECS Windows 2008 远程登陆时提示"要登录到此远程计算机,您必须被授予允许通过终端登录登录的权限",如下图所示: 问题分析 组策略中做了设置不允许管理员组成 ...