zoukankan      html  css  js  c++  java
  • Java集合中的LinkedHashMap类

    jdk1.8.0_144

      本文阅读最好先了解HashMap底层,可前往《Java集合中的HashMap类》

      LinkedHashMap由于它的插入有序特性,也是一种比较常用的Map集合。它继承了HashMap,很多方法都直接复用了父类HashMap的方法。本文将探讨LinkedHashMap的内部实现,以及它是如何保证插入元素是按插入顺序排序的。

      在分析前可以先思考下,既然是按照插入顺序,并且以Linked-开头,就很有可能是链表实现。如果纯粹以链表实现,也不是不可以,LinkedHashMap内部维护一个链表,插入一个元素则把它封装成Entry节点,并把它插入到链表尾部。功能可以实现,但这带来的查找效率达到了O(n),显然远远大于HashMap在没有冲突的情况下O(1)的时间复杂度。这就丝毫不能体现出Map这种数据结构随机存取快的优点。

      所以显然,LinkedHashMap不可能只有一个链表来维护Entry节点,它极有可能维护了两种数据结构:散列表+链表。

      为便于理解,将不会分析每个方法,会从插入开始分析LinkedHashMap的数据结构及实现。

      LinkedHashMap继承了HashMap类,并且没有重写put方法,而是直接沿用了HashMap#put方法。有关HashMap#put已经在《Java集合中的HashMap类》有了较为详细的介绍。从调用HashMap#put方法可知,它的插入过程和HashMap相同,也就是说它也一样有着和HashMap相同的散列表结构。不过要小心尽管调用的是HashMap#put方法,但在这个方法中有一个方法是构造一个新节点newNode,这里LinkedHashMap重写了,所以调用的是LinkedHashMap#newNode,也正是这个方法实现了对LinkedHashMap链表的维护。

      忽略其余代码,关键代码在HashMap#putVal中tab[i] = newNode(hash, key, value, null),稍后再来查看LinkedHashMap#newNode方法。

      其过程先用图例来说明。

      链表插入过程如下代码所示:

    1 //LinkedHashMap#newNode,构造一个新的节点
    2 Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
    3     LinkedHashMap.Entry<K,V> p =
    4         new LinkedHashMap.Entry<K,V>(hash, key, value, e);
    5     linkNodeLast(p);
    6     return p;
    7 }  
     1 //LinkedHashMap#linkNodeLast,插入到链表尾部
     2 private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
     3     LinkedHashMap.Entry<K,V> last = tail;        //LinkedHashMap定义了tail尾指针和head头指针,且链表为双向链表
     4     tail = p;
     5     if (last == null)
     6         head = p;
     7     else {
     8 //双向链表的插入
     9         p.before = last;
    10         last.after = p;
    11     }
    12 }

      对于LinkedHashMap插入,散列表部分和HashMap一致,而双向链表部分则是来一个就插到尾部,这样就保证了保持插入顺序。

      通过插入基本了解了LinkedHashMap的内部实现,get方法很简单,同样是计算出key的hash和对应散列表的下标即可。

      在LinkedHashMap还需要提到三个方法,这三个方法在HashMap中定义,但是并没有具体实现,具体实现放到了LinkedHashMap中。

    void afterNodeAccess(Node<K,V> p)

      此方法可以实现通过访问顺序排序,方法中如果定义accessOrder=true,则会将访问(get)过的元素放到链表尾部。accessOrder设置可以通过构造方法传递。

    void afterNodeInsertion(boolean evict)

      这个方法在LinkedHashMap并无意义,因为它调用的removeEldestEntry始终返回false,此时程序就会返回不会执行。但如果重写了removeEldestEntry方法,则可以实现LRU(最近最少使用)缓存。

    void afterNodeRemoval(Node<K,V> p)

      移除Map中的元素时调用,更新双向链表。

    这是一个能给程序员加buff的公众号 

  • 相关阅读:
    python--DenyHttp项目(2)--ACM监考客户端测试版☞需求分析
    python--DenyHttp项目(1)--调用cmd控制台命令os.system()
    python--DenyHttp项目(1)--GUI:tkinter☞ module 'tkinter' has no attribute 'messagebox'
    python--DenyHttp项目(1)--socket编程:服务器端进阶版socketServer
    python--DenyHttp项目(1)--socket编程:客户端与服务器端
    python生成excel格式座位表
    PythonTip--一马当先--bfs
    python pygame--倒计时
    修改Hosts文件,禁止访问指定网页
    字符串常用-----atof()函数,atoi()函数
  • 原文地址:https://www.cnblogs.com/yulinfeng/p/8590010.html
Copyright © 2011-2022 走看看