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;
        }
    }

posted on 2016-06-11 13:39  walkwalkwalk  阅读(229)  评论(0编辑  收藏  举报

导航