【集合框架】JDK1.8源码分析之HashMap & LinkedHashMap迭代器(三)
一、前言
在遍历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迭代器(三)的更多相关文章
- 【集合框架】JDK1.8源码分析之HashMap(一) 转载
[集合框架]JDK1.8源码分析之HashMap(一) 一.前言 在分析jdk1.8后的HashMap源码时,发现网上好多分析都是基于之前的jdk,而Java8的HashMap对之前做了较大的优化 ...
- 【集合框架】JDK1.8源码分析之HashMap(一)
一.前言 在分析jdk1.8后的HashMap源码时,发现网上好多分析都是基于之前的jdk,而Java8的HashMap对之前做了较大的优化,其中最重要的一个优化就是桶中的元素不再唯一按照链表组合,也 ...
- 【集合框架】JDK1.8源码分析之HashMap
一.前言 在分析jdk1.8后的HashMap源码时,发现网上好多分析都是基于之前的jdk,而Java8的HashMap对之前做了较大的优化,其中最重要的一个优化就是桶中的元素不再唯一按照链表组合,也 ...
- JDK1.8源码分析之HashMap(一) (转)
一.前言 在分析jdk1.8后的HashMap源码时,发现网上好多分析都是基于之前的jdk,而Java8的HashMap对之前做了较大的优化,其中最重要的一个优化就是桶中的元素不再唯一按照链表组合,也 ...
- JDK1.8源码分析之HashMap
一.前言 在分析jdk1.8后的HashMap源码时,发现网上好多分析都是基于之前的jdk,而Java8的HashMap对之前做了较大的优化,其中最重要的一个优化就是桶中的元素不再唯一按照链表组合,也 ...
- 【集合框架】JDK1.8源码分析HashSet && LinkedHashSet(八)
一.前言 分析完了List的两个主要类之后,我们来分析Set接口下的类,HashSet和LinkedHashSet,其实,在分析完HashMap与LinkedHashMap之后,再来分析HashSet ...
- 【集合框架】JDK1.8源码分析之ArrayList详解(一)
[集合框架]JDK1.8源码分析之ArrayList详解(一) 一. 从ArrayList字表面推测 ArrayList类的命名是由Array和List单词组合而成,Array的中文意思是数组,Lis ...
- 【集合框架】JDK1.8源码分析之Collections && Arrays(十)
一.前言 整个集合框架的常用类我们已经分析完成了,但是还有两个工具类我们还没有进行分析.可以说,这两个工具类对于我们操作集合时相当有用,下面进行分析. 二.Collections源码分析 2.1 类的 ...
- 集合之TreeSet(含JDK1.8源码分析)
一.前言 前面分析了Set接口下的hashSet和linkedHashSet,下面接着来看treeSet,treeSet的底层实现是基于treeMap的. 四个关注点在treeSet上的答案 二.tr ...
随机推荐
- mui框架中底部导航的跳转2
接上一篇 还有一种方法就是在一心得页面中打开我们所需要的网页 代码如下: 向新的的页面穿值: 获取到新页面上的值:
- ubuntu 下emacs 配置
(set-language-environment 'Chinese-GB) (set-keyboard-coding-system 'utf-8) (set-clipboard-coding-sys ...
- 关于[super dealloc]
销毁一个对象时,需要重写系统的dealloc方法来释放当前类所拥有的对象,在dealloc方法中需要先释放当前类中所有的对象,然后再调用[super dealloc]释放父类中所拥有的对象.如先调用[ ...
- CSS基础篇之选择符3
border(边框) 如何用CSS调出边框 我们给p标签加一个边框试一下 p{ border:1px solid #ccc:/*这是缩写*/ } 第一个值是为边框的宽度 第二个值是为边框线样式为直线 ...
- 一图搞定【实战Java高并发程序设计】
来了解下java并发的技术点吧.这里面包括了并发级别.算法.定律,还有开发包.在过去单核CPU时代,单任务在一个时间点只能执行单一程序,随着多核CPU的发展,并行程序开发就显得尤为重要.这本书主要介绍 ...
- c# 元组Tuple
Tuple类型像一个口袋,在出门前可以把所需的任何东西一股脑地放在里面.您可以将钥匙.驾驶证.便笺簿和钢笔放在口袋里,您的口袋是存放各种东西的收集箱.C# 4.0引入的一个新特性 Tuple类型与口袋 ...
- 一步步学习javascript基础篇(3):Object、Function等引用类型
我们在<一步步学习javascript基础篇(1):基本概念>中简单的介绍了五种基本数据类型Undefined.Null.Boolean.Number和String.今天我们主要介绍下复杂 ...
- Hadoop学习笔记—13.分布式集群中节点的动态添加与下架
开篇:在本笔记系列的第一篇中,我们介绍了如何搭建伪分布与分布模式的Hadoop集群.现在,我们来了解一下在一个Hadoop分布式集群中,如何动态(不关机且正在运行的情况下)地添加一个Hadoop节点与 ...
- Android什么时候进行View中Background的加载
对大多数Android的开发者来说,最经常的操作莫过于对界面进行布局,View中背景图片的加载是最经常做的.但是我们很少关注这个过程,这篇文章主要解析view中背景图片加载的流程.了解view中背景图 ...
- 搭建 SVN 服务器
安装.启动 SVN 服务器 在 Windows 下,可以使用以下命令将 SVN 服务注册到 windows 服务中去: sc create svnserver binPath= "drive ...