java集合类学习笔记之LinkedHashMap

Posted 路漫漫其修远兮

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java集合类学习笔记之LinkedHashMap相关的知识,希望对你有一定的参考价值。

1、简述

    LinkedHashMap是HashMap的子类,他们最大的不同是,HashMap内部维护的是一个单向的链表数组,而LinkedHashMap内部维护的是一个双向的链表数组。HashMap是无序的,LinkedHashMap可以根据访问顺序或者插入顺序进行排序(默认是根据插入顺序的,当设置accessOrder为true时会按照访问顺序排序),当按照访问顺序排序的时候,每次get或put操作时,都会将该节点放到链表的末尾

2、实现

  1、数据结构:

    LinkedHashMap是HashMap的子类,它的内部同样是使用了hashMap定义的链表数组存储的数据。只不过在HashMap中使用的是单想链表Node<k,v>作为每个节点,而LinkedHashMap使用了集成了HashMap.Node<k,v>的双向链表Entry<k,v>来作为每个节点,Entry<k,v>在Node<k,v>的基础上增加了Entry<K,V> before, after两个属性,将数组中的前后元素关联起来,这样的实现也为LinkedHashMap根据插入顺序或访问顺序提供了可能

  2、构造方法:

    LinkedHashMap是HashMap的子类,它提供了五种构造方法,

 public LinkedHashMap(int initialCapacity, float loadFactor) {
        super(initialCapacity, loadFactor);
        accessOrder = false;
    }
public LinkedHashMap(int initialCapacity) {
        super(initialCapacity);
        accessOrder = false;
    }
public LinkedHashMap() {
        super();
        accessOrder = false;
    }
public LinkedHashMap(Map<? extends K, ? extends V> m) {
        super();
        accessOrder = false;
        putMapEntries(m, false);
    }
public LinkedHashMap(int initialCapacity,
                         float loadFactor,
                         boolean accessOrder) {
        super(initialCapacity, loadFactor);
        this.accessOrder = accessOrder;
    }

    从以上我们可以看出,前面四中构造方法都是默认的accessOrder=false,即按找插入顺序排序,第五种构造方法我们可以自己定义accessOrder的值,当为true时,说明是按照访问顺序排序的,每次put或get操作的时候都会将元素移到链表的末尾

  3、LinkedHashMap操作

      由于LinkedHashMap继承了HashMap类,而且并没有重写put,get等方法,所以在这里对于LinkedHashMap的基础操作的源码就不再分析,感兴趣的可以参考上一篇关于HashMap的学习笔记。在这里重点看一下LinkedHashMap提供的removeEldestEntry(Map.Entry<K,V> eldest)方法

    

/**
     * Returns <tt>true</tt> if this map should remove its eldest entry.
     * This method is invoked by <tt>put</tt> and <tt>putAll</tt> after
     * inserting a new entry into the map.  It provides the implementor
     * with the opportunity to remove the eldest entry each time a new one
     * is added.  This is useful if the map represents a cache: it allows
     * the map to reduce memory consumption by deleting stale entries.
     *
     * <p>Sample use: this override will allow the map to grow up to 100
     * entries and then delete the eldest entry each time a new entry is
     * added, maintaining a steady state of 100 entries.
     * <pre>
     *     private static final int MAX_ENTRIES = 100;
     *
     *     protected boolean removeEldestEntry(Map.Entry eldest) {
     *        return size() &gt; MAX_ENTRIES;
     *     }
     * </pre>
     *
     * <p>This method typically does not modify the map in any way,
     * instead allowing the map to modify itself as directed by its
     * return value.  It <i>is</i> permitted for this method to modify
     * the map directly, but if it does so, it <i>must</i> return
     * <tt>false</tt> (indicating that the map should not attempt any
     * further modification).  The effects of returning <tt>true</tt>
     * after modifying the map from within this method are unspecified.
     *
     * <p>This implementation merely returns <tt>false</tt> (so that this
     * map acts like a normal map - the eldest element is never removed).
     *
     * @param    eldest The least recently inserted entry in the map, or if
     *           this is an access-ordered map, the least recently accessed
     *           entry.  This is the entry that will be removed it this
     *           method returns <tt>true</tt>.  If the map was empty prior
     *           to the <tt>put</tt> or <tt>putAll</tt> invocation resulting
     *           in this invocation, this will be the entry that was just
     *           inserted; in other words, if the map contains a single
     *           entry, the eldest entry is also the newest.
     * @return   <tt>true</tt> if the eldest entry should be removed
     *           from the map; <tt>false</tt> if it should be retained.
     */
    protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
        return false;
    }

    这个方法就是判断是否需要删除LinkedHashMap中最旧的元素,默认是false的。当我们需要利用hashmap做一些数据的缓存,可能有的数据是使用了一两次之后就失去了价值,此外我们也不想看到缓存的map无限的增长又不想自己手动的去维护这个map。此时我们就可以使用LinkedHashMap来完成这个功能;

public class Demo {
    static class MyMap<k,v> extends LinkedHashMap<k,v>{    //定义一个继承LinkedHashMap的类,并重写removeEldestEntry方法
        int size;
        public MyMap (int size){        //只提供一个有参的构造方法,用来设置map的大小
            /*
             * 这里调用的是LinkedHashMap的LinkedHashMap(int initialCapacity,boolean accessOrder)
             * 构造方法,设置按访问顺序排序
             */
            super(16,0.75f,true);        
            this.size = size;
        }
        @Override
        protected boolean removeEldestEntry(Entry<k, v> eldest) {
            // TODO Auto-generated method stub
            return this.size<size();    //设置当map里面存放的元素超过MyMap设置的size时就删除最老的元素
        }
        
    }
    public static void main(String[] args) {
        Map<String,String> map = new MyMap<String,String>(5);    
        for (int i = 0; i < 10; i++) {
            map.put(i+"", i+"");
            System.out.print(i+"="+map.size()+"\\t");
        }
        System.out.println();
        //遍历map看看里面的元素
        map.forEach((k,v)->{
            System.out.println("k="+k);
        });
        
        //此时访问一下里面的元素,再观察遍历后的顺序
        map.get("6");map.get("8");
        System.out.println("========================");
        map.forEach((k,v)->{
            System.out.println("k="+k);
        });
    }
}

打印的结果:
  

0=1 1=2 2=3 3=4 4=5 5=5 6=5 7=5 8=5 9=5
k=5
k=6
k=7
k=8
k=9
========================
k=5
k=7
k=9
k=6
k=8

 

  

总结:

    在学完LinkedHashMap和HashMap之后我们会发现,这两种集合类是非常相似的,二者都是通过内部数组去保存数据的,不同的是HashMap数组中的每个元素都是一个单向的链表,指向了产生hash冲突时和它本身具有相同hash值的元素,所以HashMap只能是无序的。LinkedHashMap中数组里面的每一个元素都是一个双向链表,它不仅指向了产生hash冲突时下一个跟它本身具有相同hash值的位置,还指定了它的上一个和下一个元素(可以是插入顺序的上一个下一个,也可以试访问顺序的上一个下一个),这样为LinkedHashMap实现有序提供了可能

以上是关于java集合类学习笔记之LinkedHashMap的主要内容,如果未能解决你的问题,请参考以下文章

Java集合深入学习总结-LinkedHashMap

深入Java集合学习系列:LinkedHashMap的实现原理

java集合类学习笔记之LinkList

死磕 java集合之LinkedHashMap源码分析

Java集合之LinkedHashMap源码分析

学习笔记之Java笔记