JDK容器学习之LinkedHashMap(二):迭代遍历的实现方式
时间:2022-04-27
本文章向大家介绍JDK容器学习之LinkedHashMap(二):迭代遍历的实现方式,主要内容包括LinkedHashMap 如何保障有序的遍历、2. 根据keys进行遍历、3. 遍历values、小结、相关博文、关注更多、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。
LinkedHashMap
如何保障有序的遍历
前一篇《JDK容器学习之LinkedHashMap (一):底层存储结构分析》 中介绍了LinkedHashMap继承自
HashMap
,且内部维护一个双向链表,那么其遍历方式是否就是对这个双向链表的遍历呢?
1. 根据Map.Entry
进行遍历
public Set<Map.Entry<K,V>> entrySet() {
Set<Map.Entry<K,V>> es;
return (es = entrySet) == null ?
(entrySet = new LinkedEntrySet()) : es;
}
final class LinkedEntrySet extends AbstractSet<Map.Entry<K,V>> {
public final int size()
{ return size; }
public final void clear()
{ LinkedHashMap.this.clear(); }
public final Iterator<Map.Entry<K,V>> iterator() {
return new LinkedEntryIterator();
}
public final boolean contains(Object o) {
// ...
}
public final boolean remove(Object o) {
// ...
}
public final Spliterator<Map.Entry<K,V>> spliterator() {
// ...
}
public final void forEach(Consumer<? super Map.Entry<K,V>> action) {
// ...
}
}
final class LinkedEntryIterator extends LinkedHashIterator
implements Iterator<Map.Entry<K,V>> {
public final Map.Entry<K,V> next() { return nextNode(); }
}
上面给出关键的链路,entrySet
方法调用,首次会创建一个LinkedEntrySet
, 内部实现迭代器 LinkedEntryIterator
所以迭代的主要逻辑就是LinkedEntryIterator
的实现方式了
abstract class LinkedHashIterator {
LinkedHashMap.Entry<K,V> next;
LinkedHashMap.Entry<K,V> current;
int expectedModCount;
LinkedHashIterator() {
// 保证从链表的头开始扫描
next = head;
expectedModCount = modCount;
current = null;
}
public final boolean hasNext() {
return next != null;
}
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;
}
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;
}
}
迭代器的实现也比较清楚,首先是在构造器中,将next
指向双向链表的头head
nextNode
方法中,返回next
节点,并将next指向链表的下一个节点
2. 根据keys进行遍历
public Set<K> keySet() {
Set<K> ks;
return (ks = keySet) == null ? (keySet = new LinkedKeySet()) : ks;
}
final class LinkedKeySet extends AbstractSet<K> {
public final int size()
{ return size; }
public final void clear()
{ LinkedHashMap.this.clear(); }
public final Iterator<K> iterator() {
return new LinkedKeyIterator();
}
public final boolean contains(Object o)
{ return containsKey(o); }
public final boolean remove(Object key) {
return removeNode(hash(key), key, null, false, true) != null;
}
public final Spliterator<K> spliterator() {
// xxx
}
public final void forEach(Consumer<? super K> action) {
// xxx
}
}
final class LinkedKeyIterator extends LinkedHashIterator
implements Iterator<K> {
public final K next() { return nextNode().getKey(); }
}
从上面的调用链来看,迭代逻辑和上面一样,唯一的区别是根据key进行迭代时,迭代器的next()
方法直接返回Node节点的key,而之前是返回整个Node节点
3. 遍历values
基本逻辑同上,省略
小结
从遍历的逻辑来看,LinkedHashMap
的遍历实际上就是遍历内部维护的双向链表
相关博文
- JDK容器学习之HashMap (一) : 底层存储结构分析
- JDK容器学习之HashMap (二) : 读写逻辑详解
- JDK容器学习之HashMap (三) : 迭代器实现
- JDK容器学习之TreeMap (一) : 底层数据结构
- JDK容器学习之TreeMap (二) : 使用说明
- JDK容器学习之LinkedHashMap (一):底层存储结构分析
关注更多
关注小灰灰blog
- 用JavaScript动态输出的JS脚本不能执行
- Dubbo源码解析 —— 服务暴露原理
- [Golang软件推荐] RSA公私钥加解密(解决Golang私钥加密公钥解密问题)
- [喵咪大数据]Hive+Hbase关联
- 再战子域共享Cookie问题
- [喵咪大数据]Presto查询引擎
- 如何在5分钟内做出你的第一个开源贡献
- [喵咪大数据]HUE大数据管理工具
- Dubbo源码解析 —— Zookeeper 订阅
- 注册中心 Eureka 源码解析 —— 项目结构简介
- 【平台】Seldon.io发布新开源平台,用于Kubernetes上的机器学习
- 分布式事务 TCC-Transaction 源码分析 —— TCC 实现
- 了解学习速率以及它如何提高深度学习的表现
- 分布式消息队列 RocketMQ源码解析:事务消息
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 唐朝人更懂React
- 一起学习PHP的runkit扩展如何使用
- CentOS7下部署开源网络流量回溯分析系统Moloch
- spring源码(八)
- 通过源码理解rarp协议(基于linux1.2.13)
- 详解Im2Col+Pack+Sgemm策略更好的优化卷积运算
- Kubernetes 使用 ceph-csi 消费 RBD 作为持久化存储
- 聊聊调试的那些事,超实用!!!
- [不定时一题]LeetCode无重复字符的最长子串
- 整理了小程序云开发实战,你看懂了吗?
- Reactive-MongoDB异步Java Driver解读
- 解密Go协程的栈内存管理
- 深入浅出mongodb之实战
- 想成为可视化高手?这篇合集就够了 | Vue
- 谈谈Vue开发过程中用到的插件