LinkedHashMap源码详解

本来是不打算先讲map的,但是随着对set集合的认识,发现如果不先搞懂各种map,是无法理解set的。因为set集合很多的底层就是用map来存储的。比如HashSet就是用HashMap,LinkedHashSet就是用LinkedHashMap。所以打算把map讲完把。

一、LinkedHashMap

先来说说它的特点,然后在一一通过分析源码来验证其实现原理

1、能够保证插入元素的顺序。深入一点讲,有两种迭代元素的方式,一种是按照插入元素时的顺序迭代,比如,插入A,B,C,那么迭代也是A,B,C,另一种是按照访问顺序,比如,在迭代前,访问了B,那么迭代的顺序就是A,C,B,比如在迭代前,访问了B,接着又访问了A,那么迭代顺序为C,B,A,比如,在迭代前访问了B,接着又访问了B,然后在访问了A,迭代顺序还是C,B,A。要说明的意思就是不是近期访问的次数最多,就放最后面迭代,而是看迭代前被访问的时间长短决定。

3、内部存储的元素的模型。entry是下面这样的,相比HashMap,多了两个属性,一个before,一个after。next和after有时候会指向同一个entry,有时候next指向null,而after指向entry。这个具体后面分析。

                    

LinkedHashMap源码详解

4、linkedHashMap和HashMap在存储操作上是一样的,但是LinkedHashMap多的东西是会记住在此之前插入的元素,这些元素不一定是在一个桶中,画个图。

                      

LinkedHashMap源码详解

也就是说,对于linkedHashMap的基本操作还是和HashMap一样,在其上面加了两个属性,也就是为了记录前一个插入的元素和记录后一个插入的元素。也就是只要和hashmap一样进行操作之后把这两个属性的值设置好,就OK了。注意一点,会有一个header的实体,目的是为了记录第一个插入的元素是谁,在遍历的时候能够找到第一个元素。

实际上存储的样子就像上面这个图一样,这里要分清楚哦。实际上的存储方式是和hashMap一样,但是同时增加了一个新的东西就是 双向循环链表。就是因为有了这个双向循环链表,LinkedHashMap才和HashMap不一样。

5、其他一些比如如何实现的循环双向链表,插入顺序和访问顺序如何实现的就看下面的详细讲解了。

二、源码分析

2.1、内部存储元素的存储结构源码和理解LinkedHashMap双向循环链表,

                    

LinkedHashMap源码详解

        

//LinkedHashMap的entry继承自HashMap的Entry。
    private static class Entry<K,V> extends HashMap.Entry<K,V> {
       
// These fields comprise the doubly linked list used for iteration.
   
//通过上面这句源码的解释,我们可以知道这两个字段,是用来给迭代时使用的,相当于一个双向链表,实际上用的时候,操作LinkedHashMap的entry和操作HashMap的Entry是一样的,只操作相同的四个属性,这两个字段是由linkedHashMap中一些方法所操作。所以LinkedHashMap的很多方法度是直接继承自HashMap。
//before:指向前一个entry元素。after:指向后一个entry元素
        Entry<K,V> before, after;
   
//使用的是HashMap的Entry构造
        Entry(int hash, K key, V value, HashMap.Entry<K,V> next) {
           
super(hash, key, value, next);
        }

//下面是维护这个双向循环链表的一些操作。在HashMap中没有这些操作,因为HashMap不需要维护,
        /**
        * Removes this entry from the linked list.
       
*/

//我们知道在双向循环链表时移除一个元素需要进行哪些操作把,比如有A,B,C,将B移除,那么A.next要指向c,c.before要指向A。下面就是进行这样的操作,但是会有点绕,他省略了一些东西。

//有的人会问,要是删除的是最后一个元素呢,那这个方法还适用吗?有这个疑问的人应该注意一下这个是双向循环链表,双向,删除哪个度适用。

        private void remove() {

      //this.before.after = this.after;

      //this.after.before = this.before; 这样看可能会更好理解,this指的就是要删除的哪个元素。


            before.after
= after;
            after.before
= before;
        }

       
/**
        * Inserts this entry before the specified existing entry in the list.
       
*/

//插入一个元素之后做的一些操作,就是将第一个元素,和最后一个元素的一些指向改变。传进来的existingEntry就是header。


        private void addBefore(Entry<K,V> existingEntry) {
            after 
= existingEntry;
            before
= existingEntry.before;
            before.after
= this;
            after.before
= this;
        }

       
/**
        * This method is invoked by the superclass whenever the value
        * of a pre-existing entry is read by Map.get or modified by Map.set.
        * If the enclosing Map is access-ordered, it moves the entry
        * to the end of the list; otherwise, it does nothing.
       
*/

//这个方法就是我们一开始说的,accessOrder为true时,就是使用的访问顺序,访问次数最少到访问次数最多,此时要做特殊处理。处理机制就是访问了一次,就将自己往后移一位,这里就是先将自己删除了,然后在把自己添加,

//这样,近期访问的少的就在链表的开始,最近访问的元素就会在链表的末尾。如果为false。那么默认就是插入顺序,直接通过链表的特点就能依次找到插入元素,不用做特殊处理。


        void recordAccess(HashMap<K,V> m) {
            LinkedHashMap
<K,V> lm = (LinkedHashMap<K,V>)m;
           
if (lm.accessOrder) {
                lm.modCount
++;
                remove();
                addBefore(lm.header);
            }
        }

       
void recordRemoval(HashMap<K,V> m) {
            remove();
        }
    }

通过查看LinkedHashMap的entry,就验证了我们上面说的特性3.

2.2、构造方法

有五个构造方法。

                

LinkedHashMap源码详解

             

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/a25696dfd150e2968980e79cb284a56a.html