java数据结构之LinkedHashMap
Posted kyleinjava
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java数据结构之LinkedHashMap相关的知识,希望对你有一定的参考价值。
一、源码注释
public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V> { /** * LinkedHashMap的节点类,在HashMap的节点的基础上增加了指向前一个节点和后一个节点的属性,来构成双向链表 */ 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); } } private static final long serialVersionUID = 3801124242820219131L; /** * 双向链表的头节点(链表中最先加入的节点) */ transient LinkedHashMap.Entry<K,V> head; /** * 双向链表的尾节点(链表中最后加入的节点) */ transient LinkedHashMap.Entry<K,V> tail; /** * 用来确定对LinkedHashMap中的节点的迭代顺序, * 如果accessOrder为true就是按照访问顺序,如果为false就按照插入顺序 */ final boolean accessOrder; // internal utilities // 将一个节点加入链表尾部 private void linkNodeLast(LinkedHashMap.Entry<K,V> p) { LinkedHashMap.Entry<K,V> last = tail; tail = p; if (last == null) head = p; else { p.before = last; last.after = p; } } // 用dst节点替换src节点 private void transferLinks(LinkedHashMap.Entry<K,V> src, LinkedHashMap.Entry<K,V> dst) { LinkedHashMap.Entry<K,V> b = dst.before = src.before; LinkedHashMap.Entry<K,V> a = dst.after = src.after; if (b == null) head = dst; else b.after = dst; if (a == null) tail = dst; else a.before = dst; } // 重新初始化 void reinitialize() { super.reinitialize(); head = tail = null; } //创建一个新的节点,并将节点加到双向链表的结尾。这是对hashMap中的newNode方法的覆盖 //所以在LinkedHashMap中put方法添加节点的同时会将该节点加到双向链表中 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; } //创建并返回一个节点,并在双向链表中用新的节点替换旧的节点 //用在对 Node<K,V> replacementNode(Node<K,V> p, Node<K,V> next) { LinkedHashMap.Entry<K,V> q = (LinkedHashMap.Entry<K,V>)p; LinkedHashMap.Entry<K,V> t = new LinkedHashMap.Entry<K,V>(q.hash, q.key, q.value, next); transferLinks(q, t); return t; } //创建一个树结点,并添加到双向链表的尾部 //用于加入将树节点加入Map中时创建节点 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; } //创建并返回一个树结点,并用该节点替换原来的节点 TreeNode<K,V> replacementTreeNode(Node<K,V> p, Node<K,V> next) { LinkedHashMap.Entry<K,V> q = (LinkedHashMap.Entry<K,V>)p; TreeNode<K,V> t = new TreeNode<K,V>(q.hash, q.key, q.value, next); transferLinks(q, t); return t; } //如果节点e被移除,那么就在双向链表中的解除前后接节点的关系 void afterNodeRemoval(Node<K,V> e) { // unlink LinkedHashMap.Entry<K,V> p = (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after; p.before = p.after = null; if (b == null) head = a; else b.after = a; if (a == null) tail = b; else a.before = b; } //覆盖HashMap中回调函数,在加入新的节点后调用,如果要实现添加新的节点后剔除最老的节点可以通过覆盖这个方法来实现 //可以实现LRU缓存策略 void afterNodeInsertion(boolean evict) { // possibly remove eldest LinkedHashMap.Entry<K,V> first; if (evict && (first = head) != null && removeEldestEntry(first)) { K key = first.key; removeNode(hash(key), key, null, false, true); } } //如果accessOrder为true,在访问过某个节点之后,将该节点放到最后的位置,相当于作为最新的节点 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; } } //仅从writeObject调用,以确保顺序一致。 void internalWriteEntries(java.io.ObjectOutputStream s) throws IOException { for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) { s.writeObject(e.key); s.writeObject(e.value); } } /** * 指定初始容量和加载因子的构造函数,并且指定迭代顺序文为插入顺序 */ public LinkedHashMap(int initialCapacity, float loadFactor) { super(initialCapacity, loadFactor); accessOrder = false; } /** * 指定初始容量和加载因子0.75的构造函数,并且指定迭代顺序文为插入顺序 */ public LinkedHashMap(int initialCapacity) { super(initialCapacity); accessOrder = false; } /** * 使用默认初始容量16和加载因子0.75的构造函数,并且指定迭代顺序文为插入顺序 */ public LinkedHashMap() { super(); accessOrder = false; } /** * 使用默认初始容量16和加载因子0.75的构造函数,并且指定迭代顺序文为插入顺序 * 将m中的键值对加入新的LinkedHashMap中 */ 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; } /** * 查询是否包含value值 */ public boolean containsValue(Object value) { for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) { V v = e.value; if (v == value || (value != null && value.equals(v))) return true; } return false; } /** * 通过key来获取value,如果accessOrder为true就将该键值对放在最后面 */ public V get(Object key) { Node<K,V> e; if ((e = getNode(hash(key), key)) == null) return null; if (accessOrder) afterNodeAccess(e); return e.value; } /** * 通过key查找值,如果没有值就返回defaultValue */ public V getOrDefault(Object key, V defaultValue) { Node<K,V> e; if ((e = getNode(hash(key), key)) == null) return defaultValue; if (accessOrder) afterNodeAccess(e); return e.value; } /** * 清空整个Map,将头节点和尾节点都设置为null */ public void clear() { super.clear(); head = tail = null; } /** * 是否允许删除最老的节点 * 通过覆盖这个方法可以实现第一固定大小的缓存集合,当集合中的元素个数大于多少时删除最老的元素 */ protected boolean removeEldestEntry(Map.Entry<K,V> eldest) { return false; } /** * 返回LinkedHashMap的key的集合视图 */ public Set<K> keySet() { Set<K> ks = keySet; if (ks == null) { ks = new LinkedKeySet(); keySet = ks; } return ks; } //LinkedHashMap key集合视图,可以对LinkedHashMap中的key进行迭代 // LinkedHashMap和LinkedKeySet会相互影响 final class LinkedKeySet extends AbstractSet<K> { public final int size() { return size; } public final void clear() { LinkedHashMap.this.clear(); } public final Iterator<K> iterator() { return new LinkedKeyIterator(); } public final boolean contains(Object o) { return containsKey(o); } public final boolean remove(Object key) { return removeNode(hash(key), key, null, false, true) != null; } public final Spliterator<K> spliterator() { return Spliterators.spliterator(this, Spliterator.SIZED | Spliterator.ORDERED | Spliterator.DISTINCT); } public final void forEach(Consumer<? super K> action) { if (action == null) throw new NullPointerException(); int mc = modCount; for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) action.accept(e.key); if (modCount != mc) throw new ConcurrentModificationException(); } } /** * LinkedHashMap的value集合视图,存有LinkedHashMap中的所有value值, * Map的值变动会影响到values这个集合中的值 */ public Collection<V> values() { Collection<V> vs = values; if (vs == null) { vs = new LinkedValues(); values = vs; } return vs; } //LinkedHashMap的value集合视图 final class LinkedValues extends AbstractCollection<V> { public final int size() { return size; } public final void clear() { LinkedHashMap.this.clear(); } public final Iterator<V> iterator() { return new LinkedValueIterator(); } public final boolean contains(Object o) { return containsValue(o); } public final Spliterator<V> spliterator() { return Spliterators.spliterator(this, Spliterator.SIZED | Spliterator.ORDERED); } public final void forEach(Consumer<? super V> action) { if (action == null) throw new NullPointerException(); int mc = modCount; for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) action.accept(e.value); if (modCount != mc) throw new ConcurrentModificationException(); } } /** * LinkedHashMap键值对集合视图 * LinkedHashMap和LinkedEntrySet的变动也是会相互影响的 */ public Set<Map.Entry<K,V>> entrySet() { Set<Map.Entry<K,V>> es; return (es = entrySet) == null ? (entrySet = new LinkedEntrySet()) : es; } final class LinkedEntrySet extends AbstractSet<Map.Entry<K,V>> { public final int size() { return size; } public final void clear() { LinkedHashMap.this.clear(); } public final Iterator<Map.Entry<K,V>> iterator() { return new LinkedEntryIterator(); } public final boolean contains(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry<?,?> e = (Map.Entry<?,?>) o; Object key = e.getKey(); Node<K,V> candidate = getNode(hash(key), key); return candidate != null && candidate.equals(e); } public final boolean remove(Object o) { if (o instanceof Map.Entry) { Map.Entry<?,?> e = (Map.Entry<?,?>) o; Object key = e.getKey(); Object value = e.getValue(); return removeNode(hash(key), key, value, true, true) != null; } return false; } public final Spliterator<Map.Entry<K,V>> spliterator() { return Spliterators.spliterator(this, Spliterator.SIZED | Spliterator.ORDERED | Spliterator.DISTINCT); } public final void forEach(Consumer<? super Map.Entry<K,V>> action) { if (action == null) throw new NullPointerException(); int mc = modCount; for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) action.accept(e); if (modCount != mc) throw new ConcurrentModificationException(); } } // 重写map的方法 //遍历LinkedHashMap中的键值对,然后做相关的操作 public void forEach(BiConsumer<? super K, ? super V> action) { if (action == null) throw new NullPointerException(); int mc = modCount; for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) action.accept(e.key, e.value); if (modCount != mc) throw new ConcurrentModificationException(); } //遍历LinkedHashMap中的键值对,并计算出新的value public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) { if (function == null) throw new NullPointerException(); int mc = modCount; for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) e.value = function.apply(e.key, e.value); if (modCount != mc) throw new ConcurrentModificationException(); } // Iterators迭代器 abstract class LinkedHashIterator { LinkedHashMap.Entry<K,V> next; LinkedHashMap.Entry<K,V> current; int expectedModCount; LinkedHashIterator() { next = head; expectedModCount = modCount; current = null; } public final boolean hasNext() { return next != null; } final LinkedHashMap.Entry<K,V> nextNode() { LinkedHashMap.Entry<K,V> e = next; if (modCount != expectedModCount) throw new ConcurrentModificationException(); if (e == null) throw new NoSuchElementException(); current = e; next = e.after; return e; } public final void remove() { Node<K,V> p = current; if (p == null) throw new IllegalStateException(); if (modCount != expectedModCount) throw new ConcurrentModificationException(); current = null; K key = p.key; removeNode(hash(key), key, null, false, false); expectedModCount = modCount; } } //key迭代器 final class LinkedKeyIterator extends LinkedHashIterator implements Iterator<K> { public final K next() { return nextNode().getKey(); } } //value迭代器 final class LinkedValueIterator extends LinkedHashIterator implements Iterator<V> { public final V next() { return nextNode().value; } } //键值对迭代器 final class LinkedEntryIterator extends LinkedHashIterator implements Iterator<Map.Entry<K,V>> { public final Map.Entry<K,V> next() { return nextNode(); } } }
二、源码解释
1、LinkedHashMap继承了HashMap,其方法和HashMap大体上差不多。但是LinkedHashMap是有序的,所以LinkedHashMap的节点在HashMap的基础上新增了before和after来分别指向前一个节点和后一个节点。在结构上构成双向链表,来实现有序。
2、LinkedHashMap通过accessOrder字段来确定集合中的节点的排序。如果accessOrder == false 那么就按照添加的先后顺序来进行排序,先加入的排在前面;如果accessOrder == true 那么就通过访问的顺序来进行排序,最近访问的排在后面。
三、LRU算法及其实现
1、 LRU是Least Recently Used的缩写,即最近最少使用。该算法基于的思想是如果一个数据最近没有被使用,那它将来被使用到的概率会比最近使用过的数据概率小。这样我们在进行缓存的时候在空间不足时就剔除一些较少使用的数据,来达到释放空间的目的。
2、通过LinkedHashMap中的accessOrder 字段可知,若将构建LinkedHashMap时将该字段设置为true,那么其内部将通过访问顺序来进行排序。这样我们就能实现LRU算法
3、使用LinkedHashMap来实现LRU算法具体代码
//继承LinkedHashMap来实现LRU缓存 public class LRULinkedHashMap<K,V> extends LinkedHashMap<K,V>{ private static final long serialVersionUID = 7164699172329853931L; //最多缓存的个数 private int limitCapacity; public LRULinkedHashMap(){ this(1024,0.75f); } //指定缓存个数 public LRULinkedHashMap(int limitCapacity){ this(limitCapacity,0.75f); } //指定缓存容量大小和加载因子 public LRULinkedHashMap(int limitCapacity, float loadFactor){ //通过计算得到容量的大小,以免发生resize浪费时间和空间 super((int)Math.ceil(limitCapacity/loadFactor) +1,loadFactor,true); this.limitCapacity = limitCapacity; } //只有当前map中的数据大于limitCapacity时才返回true protected boolean removeEldestEntry(Map.Entry<K,V> eldest) { return size() > limitCapacity; } } //测试 public class TestLRU { public static void main(String[] args) { LRULinkedHashMap<String,Integer> cacheMap = new LRULinkedHashMap<>(3); cacheMap.put("1", 1); cacheMap.put("2", 2); cacheMap.put("3", 3); cacheMap.put("4", 4); cacheMap.put("5", 6); cacheMap.put("1", 2); cacheMap.get("4"); System.out.println(cacheMap); } }
以上是关于java数据结构之LinkedHashMap的主要内容,如果未能解决你的问题,请参考以下文章