- 最初的认识:
- 可以维护元素的插入顺序,按照插入顺序遍历;
- 实现原理:LinkedHashmap的Entry除了该节点的hash值、key值、value值、next指向冲突时的后一个节点,还增加了指向前一个节点和后一个节点的指针:before、after,从而构成双向链表;除此之外,还有双向链表的头结点head和尾节点tail;
- LinkedHashMap的具体实现:
- 节点:
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); } }
- 头指针和尾指针:使用了transient关键字,该字段不进行序列化
transient LinkedHashMap.Entry<K,V> head;
transient LinkedHashMap.Entry<K,V> tail;
- 元素的两种顺序:插入顺序、访问顺序(accessorder,可以用于实现LRU cache);
- 插入顺序:遍历LinkedHashMap时,按照元素插入的顺序输出,put时,新元素追加在链表的尾部即可;
- 访问顺序:创建LinkedHashMap时,传入参数accessorder指定为true,即表示按照访问顺序遍历数组;此刻,头部是最久未被访问的节点,尾部是最近刚访问过的节点;
- get,如果该元素存在,则将节点从原位置删除,插入尾部;
- put:如果新插入的元素key已存在,则替换其value,并将它从原位置删除,再插入尾部,如果不存在,则直接插入尾部;
- 方法:removeEldestEntry
重写该方法,可以指定在put元素时删除最久未被访问的,即双向链表头部的节点,从而实现LRU cache,默认不会删除。protected boolean removeEldestEntry(Map.Entry<K,V> eldest) { return false; }
- 节点:
- LinkedHashmap的遍历
- 使用EntrySet和迭代器;该例子中使用访问顺序,所以get操作会改变节点的顺序;
public static void main(String[] args) { LinkedHashMap<String,Integer> map = new LinkedHashMap<String,Integer>(16,(float) 0.75,true); map.put("er", 1); map.put("rt", 2); map.put("ui", 3); map.put("op", 4); map.get("er"); map.get("ui"); Set<Entry<String, Integer>> set = map.entrySet(); Iterator<Entry<String, Integer>> itr = set.iterator(); while(itr.hasNext()){ Entry<String,Integer> ele = itr.next(); System.out.println(ele.getKey()+" "+ele.getValue()); } }
- 如果使用下面这种方式遍历,就会抛异常;
public static void main(String[] args) { LinkedHashMap<String,Integer> map = new LinkedHashMap<String,Integer>(16,(float) 0.75,true); map.put("er", 1); map.put("rt", 2); map.put("ui", 3); map.put("op", 4); Set<String> keyset = map.keySet(); Iterator itr = keyset.iterator(); while(itr.hasNext()){ System.out.println(map.get(itr.next())); } }
这是因为,new LinkedHashMap时,若指定accessorder为true,则get方法会调用afterNodeAccess方法,改变元素的位置,将其插入尾部,同时modCount++,所以调用Iterator 的next时,发现expectedModCount和modCount不一样,抛出ConcurrentModificationException;这是同步容器类的fail-fast机制;1 Exception in thread "main" java.util.ConcurrentModificationException at java.util.LinkedHashMap$LinkedHashIterator.nextNode(Unknown Source) at java.util.LinkedHashMap$LinkedKeyIterator.next(Unknown Source) at 验证例子.LinkedListTest.main(LinkedListTest.java:28)
- 使用LinkedHashMap实现LRU cache
- 关键是重写上面提到的removeEldestEntry方法
- 参考链接:http://www.open-open.com/lib/view/open1410490776680.html