美文网首页
Java集合框架2LinkedSHashMap

Java集合框架2LinkedSHashMap

作者: paulpaullong | 来源:发表于2017-04-06 21:52 被阅读0次

    LinkedHashMap的定义

    • 1 以jdk7为准进行说明
    package java.util;
    import java.io.*;
    public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V> {
    }
    
    • 2 可以看到LinkedHashMap继承了HashMap,所以同样适用Hash算法决定Entry在table中的索引;同时LinkedHashMap的Entry是双向链表实现的,保证了迭代顺序存放顺序一致。

    LinkedHashMap的数据结构

    • 1 通过下面代码来演示LinkedHashMap的用法
    Map<String,Integer> map = new LinkedHashMap<>();
            map.put("s1", 1);
            map.put("s2", 2);
            map.put("s3", 3);
            map.put("s4", 4);
            map.put("s5", 5);
            map.put(null, 9);
            map.put("s6", 6);
            map.put("s7", 7);
            map.put("s8", 8);
            map.put(null, 11);
            for(Map.Entry<String,Integer> entry:map.entrySet())
            {
                System.out.println(entry.getKey()+":"+entry.getValue());
            }
            System.out.println(map);
    
    • 2 运行结果为:
    s1:1
    s2:2
    s3:3
    s4:4
    s5:5
    null:11
    s6:6
    s7:7
    s8:8
    {s1=1, s2=2, s3=3, s4=4, s5=5, null=11, s6=6, s7=7, s8=8}
    
    • 3 通过结果可以看到,LinkedHashMap会记录插入的顺序,允许null的键值,当key值重复时,后面的会替换前面的。而且比HashMap多了一个头指针head(header指针是一个标记指针不存储任何数据)以及beforeafter两个指针。
    • 4 当然,如果发生hash碰撞,和HashMap一样,相同的索引出会出现一个next指向的链表结构。

    LinkedHashMap的其他特性

    • 1 LinkedHashMap还有一个私有变量accessOrder(private final boolean accessOrder;),默认为false,表示按照插入顺序遍历;譬如开篇的例子中;如果设置为true则按照访问顺序遍历,即每一次访问也会被当做一次改变来被记录下来。
    public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) {
            super(initialCapacity, loadFactor);
            this.accessOrder = accessOrder;
    }
    
    • 2 举个具体的例子
      1)代码如下
           Map<String,Integer> map = new LinkedHashMap<>(16,0.75f,true);
            map.put("s1", 1);
            map.put("s2", 2);
            map.put("s3", 3);
            map.put("s4", 4);
            map.put("s5", 5);
            map.put(null, 9);
            map.put("s6", 6);
            map.put("s7", 7);
            map.put("s8", 8);
            map.put(null, 11); // 此处null的key值set了第二次,也能改变数据存储的结果
            map.get("s6"); // 此处是get调用,也能改变数据存储的结果
            for(Map.Entry<String,Integer> entry:map.entrySet())
            {
                System.out.println(entry.getKey()+":"+entry.getValue());
            }
    

    2)运行结果

    s1:1
    s2:2
    s3:3
    s4:4
    s5:5
    s7:7
    s8:8
    null:11
    s6:6
    

    3)原因分析
    LinkedHashMap工作在按照元素访问顺序排序的模式中,get()方法会修改LinkedHashMap中的链表结构,以便将最近访问的元素放置到链表的末尾。

    • 3 该模式下的迭代方法只能使用上面的代码所示的方式,如果用下面的方式,则会抛异常。
      1)错误代码
    for(Iterator<String> iterator = map.keySet().iterator();iterator.hasNext();) {
        String name = iterator.next();
        System.out.println(name+"->"+map.get(name));
    }
    

    运行结果:

    s1->1
    Exception in thread "main" java.util.ConcurrentModificationException
        at java.util.LinkedHashMap$LinkedHashIterator.nextEntry(Unknown Source)
        at java.util.LinkedHashMap$KeyIterator.next(Unknown Source)
        at collections.map.LinkedHashMapTest.main(LinkedHashMapTest.java:33)
    

    3)原因分析
    ConcurrentModificationException异常一般会在集合迭代过程中被修改事抛出。不仅仅是LinkedHashMap,所有的集合都不允许在迭代器模式中修改集合的结构。一般认为,put()、remove()方法会修改集合的结构,因此不能在迭代器中使用。
    但该模式下的get()方法同样会改变它的存储顺序,导致抛异常。

    LinkedHashMap的典型应用

    • 1 LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。
    • 2 实现代码如下:
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.LinkedHashMap;
    import java.util.Map;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    public class LRULinkedHashMap<K, V> extends LinkedHashMap<K, V> {
    
        private static final long serialVersionUID = 7428629360694235373L;
    
        private final int maxCapacity;
    
        private static final float DEFAULT_LOAD_FACTOR = 0.75f;
    
        private final Lock lock = new ReentrantLock();
    
        public LRULinkedHashMap(int maxCapacity) {
            super(maxCapacity, DEFAULT_LOAD_FACTOR, true);
            this.maxCapacity = maxCapacity;
        }
    
        @Override
        protected boolean removeEldestEntry(java.util.Map.Entry<K, V> eldest) {
            return size() > maxCapacity;
        }
    
        @Override
        public boolean containsKey(Object key) {
            try {
                lock.lock();
                return super.containsKey(key);
            } finally {
                lock.unlock();
            }
        }
    
        @Override
        public V get(Object key) {
            try {
                lock.lock();
                return super.get(key);
            } finally {
                lock.unlock();
            }
        }
    
        @Override
        public V put(K key, V value) {
            try {
                lock.lock();
                return super.put(key, value);
            } finally {
                lock.unlock();
            }
        }
    
        public int size() {
            try {
                lock.lock();
                return super.size();
            } finally {
                lock.unlock();
            }
        }
    
        public void clear() {
            try {
                lock.lock();
                super.clear();
            } finally {
                lock.unlock();
            }
        }
    
        public Collection<Map.Entry<K, V>> getAll() {
            try {
                lock.lock();
                return new ArrayList<Map.Entry<K, V>>(super.entrySet());
            } finally {
                lock.unlock();
            }
        }
    
        public static void main(String[] args) {
            LRULinkedHashMap<String, String> lruMap = new LRULinkedHashMap<>(4);
            lruMap.put("first", "1");
            lruMap.put("second", "2");
            lruMap.put("third", "3");
            lruMap.put("fourth", "4");
                    lruMap.put("fivth", "5");
            lruMap.get("fivth");
            lruMap.get("fourth");
            lruMap.get("third");        
                    lruMap.get("second");
    
            for (Map.Entry<String, String> entry : lruMap.entrySet()) {
                System.out.println(entry.getKey() + ":" + entry.getValue());
            }
        }
    }
    
    • 3 运行结果
    first:1
    fourth:4
    second:two
    fivth:5
    
    • 4 原因分析
      1)设置了maxCapacity,即该map只保存最多maxCapacity多个Entry。
      2)由于是访问模式,所以最近访问的被放到链表的尾部。

    相关文章

      网友评论

          本文标题:Java集合框架2LinkedSHashMap

          本文链接:https://www.haomeiwen.com/subject/vntcattx.html