源码剖析之LinkedHashMap

简介: LinkedHashMap是一个有序的map集合,他的特点就是在map的基础上增加了顺序从而让无序的map成为一个有序的集合,同时LinkedHashMap底层的实现也非常有意思。

LinkedHashMap源码

​ LinkedHashMap是一个有序的map集合,他的特点就是在map的基础上增加了顺序从而让无序的map成为一个有序的集合,同时LinkedHashMap底层的实现也非常有意思。

一、继承树与构造方法

public class LinkedHashMap<K,V>extends HashMap<K,V> implements Map<K,V>

​ LinkedHashMap继承了HashMap,也就是说很多的Map接口LinkedHashMap自己并没有实现而是使用的HashMap的实现,当然他自己也重写了很多的方法。

// 默认的构造方法   
public LinkedHashMap() {
   
   
        super();
        accessOrder = false;
}

​ 从构造方法中可以看出LinkedHashMap使用super方法调用HashMap进行的初始化操作。

accessOrder:是否能改变顺序,如果设置位true,那么在修改数据和获取数据以后他会进入到链表的尾部一般该值我们都是使用默认的,大家也不要轻易的修改它

二、put方法添加数据

// 直接调用HashMap的put方法添加数据    
public V put(K key, V value) {
   
   
    return putVal(hash(key), key, value, false, true);
}

​ 从方法的调用来看LinkedHashMap和HashMap使用的是同一个方法那么LinkedHashMap是如何来维护插入的顺序的呢?

​ 主要是LinkedHashMap重写了HashMap中的newNode方法,在方法中除了创建一个Node节点以为还使用双链表的方式来维护插入的顺序

    Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
   
   
        LinkedHashMap.Entry<K,V> p =
            new LinkedHashMap.Entry<K,V>(hash, key, value, e);
        linkNodeLast(p); //向双链表中插入一个Entry节点对象
        return p;
    }

    //维护LinkedHashMap顺序的双链表
    private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
   
   
        LinkedHashMap.Entry<K,V> last = tail;
        tail = p;
        if (last == null)
            head = p;
        else {
   
   
            p.before = last;
            last.after = p;
        }
    }

三、iterator迭代的时候如何保证顺序

// 迭代的代码
Iterator iterator = linkedHashMap.keySet().iterator();
while(iterator.hasNext()){
   
   
    Object next = iterator.next();
}
// 当调用next方法的时候会进入到LinkedHashMap重写的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;
}

​ 从上面的代码我们可以看到他的key的迭代的时候并没有调用HashMap提供的那个方法进行迭代而是自己重写了nextNode方法,然后再nextNode方法中变量LinkedHashMap所维护的那个双链表,而该链表正好维护了插入的顺序,所以取出来的时候自己也是有序的。

四、fail fast机制

​ fail fast机制其实在很多的集合中都有这个机制,主要的表现就是如果我们在迭代的过程中删除了元素那么系统就会抛出ConcurrentModificationException的异常。

​ fail fast机制是如何实现这个概念的呢?

​ 一个线程要迭代一个集合的时候会首先获取他的modCount,并且再迭代的时候会一直不停的判断该值是否背修改了如果被修改了则会抛出并发修改异常。

final LinkedHashMap.Entry<K,V> nextNode() {
   
   
    LinkedHashMap.Entry<K,V> e = next;
    // 每次迭代都会检测modCount 是否背修改如果被修改则抛出并发修改异常
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
    if (e == null)
        throw new NoSuchElementException();
    current = e;
    next = e.after;
    return e;
}

五、LinkedHashSet/TreeSet/HashSet底层实现

  1. HashSet底层使用的是HashMap来实现的,只是只用到了他的key,value统一使用的是一个Object类型的对象,且HashMap是无序的所以HashSet也是无序的
  2. TreeSet 底层使用的是TreeMap的key,同时可以传递一个Comparator接口进行排序
  3. LinkedHashSet 底层使用的是LinkedHashMap所以该set是有序的且顺序是通过一个双向链表来实现的
目录
相关文章
|
Android开发 开发者
什么是Android Jetpack,它包括哪些组件?
什么是Android Jetpack,它包括哪些组件?
548 0
|
SQL 关系型数据库 MySQL
MySQL数据库中给表添加字段并设置备注的脚本编写
通过上述步骤,你可以在MySQL数据库中给表成功添加新字段并为其设置备注。这样的操作对于保持数据库结构的清晰和最新非常重要,同时也帮助团队成员理解数据模型的变化和字段的具体含义。在实际操作中,记得调整脚本以适应具体的数据库和表名称,以及字段的详细规范。
490 8
|
Java
IDEA 报错:org.apache.jasper.JasperException:无法在web.xml或使用此应用程序部署的jar文件中解析绝对uri
IDEA 报错:org.apache.jasper.JasperException:无法在web.xml或使用此应用程序部署的jar文件中解析绝对uri
2972 0
IDEA 报错:org.apache.jasper.JasperException:无法在web.xml或使用此应用程序部署的jar文件中解析绝对uri
|
消息中间件 存储 Java
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
Android 消息处理机制估计都被写烂了,但是依然还是要写一下,因为Android应用程序是通过消息来驱动的,Android某种意义上也可以说成是一个以消息驱动的系统,UI、事件、生命周期都和消息处理机制息息相关,并且消息处理机制在整个Android知识体系中也是尤其重要,在太多的源码分析的文章讲得比较繁琐,很多人对整个消息处理机制依然是懵懵懂懂,这篇文章通过一些问答的模式结合Android主线程(UI线程)的工作原理来讲解,源码注释很全,还有结合流程图,如果你对Android 消息处理机制还不是很理解,我相信只要你静下心来耐心的看,肯定会有不少的收获的。
908 3
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
|
监控 UED 开发者
鸿蒙next版开发:订阅应用事件(ArkTS)
在HarmonyOS 5.0中,ArkTS引入了强大的应用事件订阅机制,允许开发者订阅和处理系统或应用级别的事件,这对于监控应用行为、优化用户体验和进行性能分析至关重要。本文详细介绍了如何在ArkTS中订阅应用事件,并提供了示例代码,包括导入模块、创建观察者、设置事件参数等步骤。通过这些方法,开发者可以更智能地管理和响应应用事件。
614 11
|
架构师 Java 开发者
得物面试:Springboot自动装配机制是什么?如何控制一个bean 是否加载,使用什么注解?
在40岁老架构师尼恩的读者交流群中,近期多位读者成功获得了知名互联网企业的面试机会,如得物、阿里、滴滴等。然而,面对“Spring Boot自动装配机制”等核心面试题,部分读者因准备不足而未能顺利通过。为此,尼恩团队将系统化梳理和总结这一主题,帮助大家全面提升技术水平,让面试官“爱到不能自已”。
得物面试:Springboot自动装配机制是什么?如何控制一个bean 是否加载,使用什么注解?
|
存储 缓存 Java
Android性能优化:内存管理与LeakCanary技术详解
【7月更文挑战第21天】内存管理是Android性能优化的关键部分,而LeakCanary则是进行内存泄漏检测和修复的强大工具。
|
负载均衡 Java API
【GateWay快速入门】 —— 每天一点小知识
【GateWay快速入门】 —— 每天一点小知识
840 0
|
缓存 开发者 网络架构
ArkTS 函数方法基础:掌握核心编程概念
本文深入探讨了ArkTS中的函数和方法,涵盖函数声明、参数处理、返回类型、作用域、函数类型等内容,帮助开发者掌握ArkTS的核心编程概念。通过实例讲解可选参数、rest参数、默认参数等特性,并讨论函数在面向对象编程、性能优化和错误处理中的应用,助力开发者编写更健壮、可维护的代码。
910 0
|
存储 Java
打破常规!HashSet和TreeSet教你重新认识Java集合的无序与有序
【6月更文挑战第17天】Java集合框架中的Set接口,HashSet无序而TreeSet有序。HashSet基于哈希表,元素插入顺序不可预测,适合快速去重。TreeSet利用红黑树保证有序性,支持自然排序或自定义排序。若需同时无序和有序,可先用HashSet去重,再将元素加入TreeSet,但会牺牲性能。选择时依据对顺序和性能的需求。
468 2