• LinkedHashMap


    LinkedHashMap

    在java中,所有加了Link前缀的数据结构,都是可以按照顺序进行访问的,例如LinkedHashMap。LinkedHashMap和HashMap之间的区别在于它使用了一个双向链表将所有的entries链接起来。这个双向链表默认是按照entry的插入先后顺序构成的,如果重复插入一个key值相同的entry是不会影响链表的顺序的哦。

    LinkedHashMap也不是线程安全的,如果想要在多线程环境中使用,应该在构造的时候使用如下的语句:

    Map m = Collections.synchronizedMap(new LinkedHashMap(...));
    

    LinkedHashMap也是fail-fast的,如果在迭代过程中,LinkedHashMap的结构发生了变化,就会抛出ConcurrentModificationException异常。

    因为HashMap现在已经改为链表和红黑树两种存储方式了,LinkedHashMap作为HashMap的子类,也改变了自己的存储方式,所以LinkedHashMap不是按照链表来存储的。

    使用

    • 默认按照插入顺序排序
    import java.util.Iterator;
    import java.util.LinkedHashMap;
    import java.util.Map;
    
    public class LinkedHashMapTest {
        public static void main(String[] args) {
            Map<String, Integer> test_map = new LinkedHashMap<String, Integer>();
    //        Map<String, Integer> test_map = new LinkedHashMap<String, Integer>(20, 0.7F, true);
            test_map.put("人类", 1);
            test_map.put("泰坦", 10);
            test_map.put("兽人", 2);
            test_map.put("精灵", 3);
    
            test_map.get("兽人");
            Iterator iter = test_map.entrySet().iterator();
    
            while (iter.hasNext()) {
                Map.Entry entry = (Map.Entry) iter.next();
                System.out.println(entry.getKey() + " : " + entry.getValue());
            }
        }
    }
    

    输出:

    人类 : 1
    泰坦 : 10
    兽人 : 2
    精灵 : 3
    

    上面的输出是按照输入的顺序来的,如果将LinkedHashMap变为HashMap就会发现输出的是一个无序的顺序了。

    • LinkedHashMap还有一种排序方式,类似于LRU,最近最久未使用,会将最不常用的entry排在最前。
    public class LinkedHashMapTest {
        public static void main(String[] args) {
    //        Map<String, Integer> test_map = new HashMap<String, Integer>();
            Map<String, Integer> test_map = new LinkedHashMap<String, Integer>(20, 0.7F, true);
            test_map.put("人类", 1);
            test_map.put("泰坦", 10);
            test_map.put("兽人", 2);
            test_map.put("精灵", 3);
    
            test_map.get("兽人");
            test_map.get("人类");
            Iterator iter = test_map.entrySet().iterator();
    
            while (iter.hasNext()) {
                Map.Entry entry = (Map.Entry) iter.next();
                System.out.println(entry.getKey() + " : " + entry.getValue());
            }
        }
    }
    

    输出:

    泰坦 : 10
    精灵 : 3
    兽人 : 2
    人类 : 1
    

    实现介绍

    实现方面,LinkedHashMap是在HashMap的基础上增加了对列表的操作。

    • Entry
        static class Entry<K,V> extends HashMap.Node<K,V> {
            Entry<K,V> before, after;
            Entry(int hash, K key, V value, Node<K,V> next) {
                super(hash, key, value, next);
            }
        }
    

    Entry的结构如上,每个Entry多了一个before 和 after 变量,分别指向链表前后的节点。

    • 构造函数

    LinkedHashMap的构造函数中有一个参数可以控制是否按照访问顺序进行排序,不过只有下面这个构造函数才可以使用,
    也就是必须同时传三个参数才可以。

        public LinkedHashMap(int initialCapacity,
                             float loadFactor,
                             boolean accessOrder) {
            super(initialCapacity, loadFactor);
            this.accessOrder = accessOrder;
        }
    
    • 将entry添加到链表中

    LinkedHashMap并不是在PUT函数中将entry添加到链表中,而是在新建entry(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);
            return p;
        }
        
        TreeNode<K,V> newTreeNode(int hash, K key, V value, Node<K,V> next) {
            TreeNode<K,V> p = new TreeNode<K,V>(hash, key, value, next);
            linkNodeLast(p);
            return p;
        }
    
    • 那么这个访问顺序修改是怎么实现的呢,它是在get函数中修改了链表的结构。
        public V get(Object key) {
            Node<K,V> e;
            if ((e = getNode(hash(key), key)) == null)
                return null;
            //如果accessOrder为真,就修改链表的顺序,将它放到链表的最后
            if (accessOrder)
                afterNodeAccess(e);
            return e.value;
        }
    	
    	void afterNodeAccess(Node<K,V> e) { // move node to last
            LinkedHashMap.Entry<K,V> last;
            //如果这个节点不是最后的节点,就将该节点放到最后
            if (accessOrder && (last = tail) != e) {
                LinkedHashMap.Entry<K,V> p =
                    (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
                p.after = null;
                if (b == null)
                    head = a;
                else
                    b.after = a;
                if (a != null)
                    a.before = b;
                else
                    last = b;
                if (last == null)
                    head = p;
                else {
                    p.before = last;
                    last.after = p;
                }
                tail = p;
                ++modCount;
            }
        }
    
  • 相关阅读:
    JavaScript 之 typeof
    Octotree Chrome安装与使用方法
    支持主流MySQL中间件的数据迁移工具ora2mysql
    Eclipse搭建SpringBoot之HelloWorld
    Spring Boot插件spring tool suite安装及使用
    树的前中后序遍历非递归实现
    判断是否是完全二叉树
    Leetcode 437_path sum III todo
    DFS回溯只在递归基回溯————leetcode112
    Leetcode 94 Binary Tree Inorder
  • 原文地址:https://www.cnblogs.com/SpeakSoftlyLove/p/5575207.html
Copyright © 2020-2023  润新知