源码剖析之LinkedHashMap

本文涉及的产品
Serverless 应用引擎 SAE,800核*时 1600GiB*时
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
性能测试 PTS,5000VUM额度
简介: 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是有序的且顺序是通过一个双向链表来实现的
目录
相关文章
|
存储 机器学习/深度学习 算法
源码剖析之ConcurrentHashMap
​ JDK8中ConcurrentHashMap的结构是:数组+链表+红黑树。 ​ 因为在hash冲突严重的情况下,链表的查询效率是O(n),所以jdk8中改成了单个链表的个数大于8时,数组长度小于64就扩容,数组长度大于等于64,则链表会转换为红黑树,这样以空间换时间,查询效率会变O(nlogn)。 ​ 红黑树在Node数组内部存储的不是一个TreeNode对象,而是一个TreeBin对象,TreeBin内部维持着一个红黑树。 ​ 在JDK8中ConcurrentHashMap最经点的实现是使用CAS+synchronized+volatile 来保证并发安全
107 0
源码剖析之ConcurrentHashMap
|
存储 算法 Java
HashSet源码剖析
HashSet源码剖析
53 0
Java集合源码剖析——基于JDK1.8中HashSet、LinkedHashSet的实现原理
Java集合源码剖析——基于JDK1.8中HashSet、LinkedHashSet的实现原理
Java集合源码剖析——基于JDK1.8中HashSet、LinkedHashSet的实现原理
|
存储 算法 Java
Java集合源码剖析——基于JDK1.8中HashMap的实现原理(上)
Java集合源码剖析——基于JDK1.8中HashMap的实现原理(上)
Java集合源码剖析——基于JDK1.8中HashMap的实现原理(上)
Java集合源码剖析——基于JDK1.8中HashMap的实现原理(下)
Java集合源码剖析——基于JDK1.8中HashMap的实现原理(下)
Java集合源码剖析——基于JDK1.8中HashMap的实现原理(下)
|
存储 Java
HashMap源码解读
带你走进HashMap,了解HashMap的特性和HashMap源码解读(jdk7)。
160 0
HashMap源码解读