14 java.util.LinkedHashMap
来源:互联网 发布:mastercam编程步骤 编辑:程序博客网 时间:2024/05/02 21:31
LinkedHashMap
2015.01.28 By 970655147
备注 : “[” 和”]”之间的内容是 我添加的
Map 接口的哈希表和链接列表实现,具有可预知的迭代顺序。此实现与 HashMap 的不同之处在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,该迭代顺序通常就是将键插入到映射中的顺序(插入顺序)。注意,如果在映射中重新插入 键,则插入顺序不受影响。(如果在调用 m.put(k, v) 前 m.containsKey(k) 返回了 true,则调用时会将键 k 重新插入到映射 m 中。)
此实现可以让客户避免未指定的、由 HashMap(及 Hashtable)所提供的通常为杂乱无章的排序工作,同时无需增加与 TreeMap 相关的成本。使用它可以生成一个与原来顺序相同的映射副本,而与原映射的实现无关:
void foo(Map m) { Map copy = new LinkedHashMap(m); ... }
如果模块通过输入得到一个映射,复制这个映射,然后返回由此副本确定其顺序的结果,这种情况下这项技术特别有用。(客户通常期望返回的内容与其出现的顺序相同。)
提供特殊的构造方法[可配置accessOrder属性的构造方法]来创建链接哈希映射,该哈希映射的迭代顺序就是最后访问其条目的顺序,从近期访问最少到近期访问最多的顺序(访问顺序)。这种映射很适合构建 LRU 缓存。调用 put 或 get 方法将会访问相应的条目(假定调用完成后它还存在)。putAll 方法以指定映射的条目集迭代器提供的键-值映射关系的顺序,为指定映射的每个映射关系生成一个条目访问。任何其他方法均不生成条目访问。[这里 我的理解 : 所谓的指定映射是指原集合中存在的映射]特别是,collection 视图上的操作不 影响底层映射的迭代顺序。
可以重写 removeEldestEntry(Map.Entry) 方法来实施策略,以便在将新映射关系添加到映射时自动移除旧的映射关系。
此类提供所有可选的 Map 操作,并且允许 null 元素。与 HashMap 一样,它可以为基本操作(add、contains 和 remove)提供稳定的性能,假定哈希函数将元素正确分布到桶中。由于增加了维护链接列表的开支,其性能很可能比 HashMap 稍逊一筹,不过这一点例外:LinkedHashMap 的 collection 视图迭代所需时间与映射的大小 成比例。HashMap 迭代时间很可能开支较大,因为它所需要的时间与其容量 成比例。[最后一点, 详见HashMap, 以及此处的迭代方式]
链接的哈希映射具有两个影响其性能的参数:初始容量和加载因子。它们的定义与 HashMap 极其相似。要注意,为初始容量选择非常高的值对此类的影响比对 HashMap 要小,因为此类的迭代时间不受容量的影响。
注意,此实现不是同步的。如果多个线程同时访问链接的哈希映射,而其中至少一个线程从结构上修改了该映射,则它必须 保持外部同步。这一般通过对自然封装该映射的对象进行同步操作来完成。如果不存在这样的对象,则应该使用 Collections.synchronizedMap 方法来“包装”该映射。最好在创建时完成这一操作,以防止对映射的意外的非同步访问:
Map m = Collections.synchronizedMap(new LinkedHashMap(...));**结构修改是指添加或删除一个或多个映射关系,或者在按访问顺序链接的哈希映射中影响迭代顺序的任何操作。在按插入顺序链接的哈希映射中,仅更改与映射中已包含键关联的值不是结构修改。在按访问顺序链接的哈希映射中,仅利用 get 查询映射不是结构修改。[这里 详见Entry.recordAccess])**
Collection(由此类的所有 collection 视图方法所返回)的 iterator 方法返回的迭代器都是快速失败 的:在迭代器创建之后,如果从结构上对映射进行修改,除非通过迭代器自身的 remove 方法,其他任何时间任何方式的修改,迭代器都将抛出 ConcurrentModificationException。因此,面对并发的修改,迭代器很快就会完全失败,而不冒将来不确定的时间任意发生不确定行为的风险。
注意,迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。快速失败迭代器会尽最大努力抛出 ConcurrentModificationException。因此,编写依赖于此异常的程序的方式是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测程序错误。
此类是 Java Collections Framework 的成员。
现在 我们来思考一下, 假如给你一个 :
public class MyLinkedHashMap extends HashMap
那么 你应该怎么来实现保存插入顺序呢 ?
我说一下 我的思路 :
1. 使用一个数据结构保存插入顺序 到对应的条目的映射
2. 从每一个结点下手, 每一个结点保存一些额外的信息[保存插入顺序的下一个结点的引用, 用于迭代, 相当于多维护了一个链表]
对于思路一 : 其实从效率上来说就像一个ArrayList, 当在插入顺序中间加上一个 或者删去一个结点, 都需要将该索引以及之后的数据向后 或者向前移动, 所以 对于上面的情况来说, 效率是比较地低下的, 不过好在能够随机存取数据
对于思路二 : 其实和思路一相比, 就像ArrayList 和LinkedList的区别, 优势在于在插入顺序中间插入 或者删除一个数据效率比较高, 不过不能够随机存取数据
jdk中LinkedHashMap的实现是继承自HashMap 并在结点中加入一个before, after 来保存当前结点的前一个结点和下一个结点的引用, 并且在添加数据, 删除数据的时候额外的初更新这两个数据, 来维护链表的性质
还有一个优化就是迭代数据的时候[迭代, 查找是否存在指定的Value], 使用插入顺序来迭代, 这样的话, 迭代的效率就只与集合中元素的个数有关, 而与集合的capacity没有关系
start ->
声明
/** * <p>Hash table and linked list implementation of the <tt>Map</tt> interface, * with predictable iteration order. This implementation differs from * <tt>HashMap</tt> in that it maintains a doubly-linked list running through * all of its entries. This linked list defines the iteration ordering, * which is normally the order in which keys were inserted into the map * (<i>insertion-order</i>). Note that insertion order is not affected * if a key is <i>re-inserted</i> into the map. (A key <tt>k</tt> is * reinserted into a map <tt>m</tt> if <tt>m.put(k, v)</tt> is invoked when * <tt>m.containsKey(k)</tt> would return <tt>true</tt> immediately prior to * the invocation.) * * <p>This implementation spares its clients from the unspecified, generally * chaotic ordering provided by {@link HashMap} (and {@link Hashtable}), * without incurring the increased cost associated with {@link TreeMap}. It * can be used to produce a copy of a map that has the same order as the * original, regardless of the original map's implementation: * <pre> * void foo(Map m) { * Map copy = new LinkedHashMap(m); * ... * } * </pre> * This technique is particularly useful if a module takes a map on input, * copies it, and later returns results whose order is determined by that of * the copy. (Clients generally appreciate having things returned in the same * order they were presented.) * * <p>A special {@link #LinkedHashMap(int,float,boolean) constructor} is * provided to create a linked hash map whose order of iteration is the order * in which its entries were last accessed, from least-recently accessed to * most-recently (<i>access-order</i>). This kind of map is well-suited to * building LRU caches. Invoking the <tt>put</tt> or <tt>get</tt> method * results in an access to the corresponding entry (assuming it exists after * the invocation completes). The <tt>putAll</tt> method generates one entry * access for each mapping in the specified map, in the order that key-value * mappings are provided by the specified map's entry set iterator. <i>No * other methods generate entry accesses.</i> In particular, operations on * collection-views do <i>not</i> affect the order of iteration of the backing * map. * * <p>The {@link #removeEldestEntry(Map.Entry)} method may be overridden to * impose a policy for removing stale mappings automatically when new mappings * are added to the map. * * <p>This class provides all of the optional <tt>Map</tt> operations, and * permits null elements. Like <tt>HashMap</tt>, it provides constant-time * performance for the basic operations (<tt>add</tt>, <tt>contains</tt> and * <tt>remove</tt>), assuming the hash function disperses elements * properly among the buckets. Performance is likely to be just slightly * below that of <tt>HashMap</tt>, due to the added expense of maintaining the * linked list, with one exception: Iteration over the collection-views * of a <tt>LinkedHashMap</tt> requires time proportional to the <i>size</i> * of the map, regardless of its capacity. Iteration over a <tt>HashMap</tt> * is likely to be more expensive, requiring time proportional to its * <i>capacity</i>. * * <p>A linked hash map has two parameters that affect its performance: * <i>initial capacity</i> and <i>load factor</i>. They are defined precisely * as for <tt>HashMap</tt>. Note, however, that the penalty for choosing an * excessively high value for initial capacity is less severe for this class * than for <tt>HashMap</tt>, as iteration times for this class are unaffected * by capacity. * * <p><strong>Note that this implementation is not synchronized.</strong> * If multiple threads access a linked hash map concurrently, and at least * one of the threads modifies the map structurally, it <em>must</em> be * synchronized externally. This is typically accomplished by * synchronizing on some object that naturally encapsulates the map. * * If no such object exists, the map should be "wrapped" using the * {@link Collections#synchronizedMap Collections.synchronizedMap} * method. This is best done at creation time, to prevent accidental * unsynchronized access to the map:<pre> * Map m = Collections.synchronizedMap(new LinkedHashMap(...));</pre> * * A structural modification is any operation that adds or deletes one or more * mappings or, in the case of access-ordered linked hash maps, affects * iteration order. In insertion-ordered linked hash maps, merely changing * the value associated with a key that is already contained in the map is not * a structural modification. <strong>In access-ordered linked hash maps, * merely querying the map with <tt>get</tt> is a structural * modification.</strong>) * * <p>The iterators returned by the <tt>iterator</tt> method of the collections * returned by all of this class's collection view methods are * <em>fail-fast</em>: if the map is structurally modified at any time after * the iterator is created, in any way except through the iterator's own * <tt>remove</tt> method, the iterator will throw a {@link * ConcurrentModificationException}. Thus, in the face of concurrent * modification, the iterator fails quickly and cleanly, rather than risking * arbitrary, non-deterministic behavior at an undetermined time in the future. * * <p>Note that the fail-fast behavior of an iterator cannot be guaranteed * as it is, generally speaking, impossible to make any hard guarantees in the * presence of unsynchronized concurrent modification. Fail-fast iterators * throw <tt>ConcurrentModificationException</tt> on a best-effort basis. * Therefore, it would be wrong to write a program that depended on this * exception for its correctness: <i>the fail-fast behavior of iterators * should be used only to detect bugs.</i> * * <p>This class is a member of the * <a href="{@docRoot}/../technotes/guides/collections/index.html"> * Java Collections Framework</a>. * * @param <K> the type of keys maintained by this map * @param <V> the type of mapped values * * @author Josh Bloch * @see Object#hashCode() * @see Collection * @see Map * @see HashMap * @see TreeMap * @see Hashtable * @since 1.4 */public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>
这里 我们只说明一些相比于HashMap此类的不同, 比如 增加了那些属性, 重写了什么方法, 增加了什么方法 等等
LinkedHashMap. 属性
// 序列化id// 头结点, 用于遍历整个集合, 以此结点为终点// 迭代的顺序 如果为true则以访问的顺序[get 或者set访问之后会将该元素”移动”到最后],false则以插入元素的顺序 private static final long serialVersionUID = 3801124242820219131L; private transient Entry<K,V> header; private final boolean accessOrder;
LinkedHashMap. LinkedHashMap()
public LinkedHashMap(int initialCapacity, float loadFactor) { super(initialCapacity, loadFactor); accessOrder = false;}public LinkedHashMap(int initialCapacity) { super(initialCapacity); accessOrder = false;}public LinkedHashMap() { super(); accessOrder = false;}public LinkedHashMap(Map<? extends K, ? extends V> m) { super(m); accessOrder = false;}public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) { super(initialCapacity, loadFactor); this.accessOrder = accessOrder;}
LinkedHashMap. transfer(HashMap.Entry[] newTable, boolean rehash)
@Override void transfer(HashMap.Entry[] newTable, boolean rehash) { int newCapacity = newTable.length; // 重写自HashMap 将 // 遍历当前LinkedHashMap // 如果要重新计算hashCode 则重新计算 // 根据index 将e添加到指定的bucket中 // 这里与HashMap的不同在于, 遍历所有的元素的方式不同, HashMap是使用遍历每一个桶的链表, 而这里是从header.after开始遍历 for (Entry<K,V> e = header.after; e != header; e = e.after) { if (rehash) e.hash = (e.key == null) ? 0 : hash(e.key); int index = indexFor(e.hash, newCapacity); e.next = newTable[index]; newTable[index] = e; } }
LinkedHashMap. containsValue(Object value)
public boolean containsValue(Object value) { // 重写该方法 使用更快的迭代方式 [因为 HashMap的桶的个数对于元素的遍历存在影响, 而这里直接使用header遍历, 就与桶的个数无关了, 效率比HashMap的遍历高一些] // 对于null元素 “==” 判定 // 否则 equals 判定 // Overridden to take advantage of faster iterator if (value==null) { for (Entry e = header.after; e != header; e = e.after) if (e.value==null) return true; } else { for (Entry e = header.after; e != header; e = e.after) if (value.equals(e.value)) return true; } return false; }
LinkedHashMap. clear()
public void clear() { // super.clear() + 更新header的after, before引用 super.clear(); header.before = header.after = header; }
LinkedHashMap. get(Object key)
public V get(Object key) { // 使用从HashMap中继承的getEntry方法 找到key对应的entry // 如果没找到 // 如果accessOrder为true 则将该元素”移动”到该LinkedHashMap的最后面 Entry<K,V> e = (Entry<K,V>)getEntry(key); if (e == null) return null; e.recordAccess(this); return e.value; }HashMap. getEntry(Object key) final Entry<K,V> getEntry(Object key) { if (size == 0) { return null; } int hash = (key == null) ? 0 : hash(key); for (Entry<K,V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } return null; }LinkedHashMap$Entry. recordAccess(HashMap<K,V> m) void recordAccess(HashMap<K,V> m) { LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m; // 如果accessOrder为true 则将该元素”移动”到最后[最后遍历] if (lm.accessOrder) { lm.modCount++; remove(); addBefore(lm.header); } }LinkedHashMap$Entry.remove() // “移除”该元素[更改after,before等元素 由recordRemoval调用] private void remove() { before.after = after; after.before = before; }LinkedHashMap$Entry. addBefore(Entry<K,V> existingEntry) private void addBefore(Entry<K,V> existingEntry) { // 本entry的after指向existingEntry before指向existingEntry的前一个元素 // existingEntry的前一个元素.after指向当前元素 // existingEntry.before指向当前元素 after = existingEntry; before = existingEntry.before; before.after = this; after.before = this; }
Entry. addBefore
LinkedHashMap.get(Object key)
LinkedHashMap. addEntry(int hash, K key, V value, int bucketIndex)
This override alters behavior of superclass put method. It causes newly * allocated entry to get inserted at the end of the linked list and * removes the eldest entry if appropriate. void addEntry(int hash, K key, V value, int bucketIndex) { super.addEntry(hash, key, value, bucketIndex); // Remove eldest entry if instructed Entry<K,V> eldest = header.after; // 没看懂这个是干什么的.. // 是否删除最久未使用的元素[第一个元素][LRU机制] 应该在accessOrder为true的时候有一些作用吧.. if (removeEldestEntry(eldest)) { removeEntryForKey(eldest.key); } }LinkedHashMap. removeEldestEntry(Map.Entry<K,V> eldest) protected boolean removeEldestEntry(Map.Entry<K,V> eldest) { return false; }LinkedHashMap. createEntry(int hash, K key, V value, int bucketIndex)void createEntry(int hash, K key, V value, int bucketIndex) { // 新建一个entry 其next为table[bucketIndex]// 将其添加到header之前 成为输入顺序的最后一个元素 HashMap.Entry<K,V> old = table[bucketIndex]; Entry<K,V> e = new Entry<>(hash, key, value, old); table[bucketIndex] = e; e.addBefore(header); size++; }
LinkedHashMap. put(K key, V value) :
LinkedHashMap. putAll(Map< ? extends K,? extends V> m)
HashMap. remove(Object key)
public V remove(Object key) { Entry<K,V> e = removeEntryForKey(key); return (e == null ? null : e.value);}HashMap. removeEntryForKey(Object key)final Entry<K,V> removeEntryForKey(Object key) { if (size == 0) { return null; } int hash = (key == null) ? 0 : hash(key); int i = indexFor(hash, table.length); Entry<K,V> prev = table[i]; Entry<K,V> e = prev; while (e != null) { Entry<K,V> next = e.next; Object k; if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) { modCount++; size--; if (prev == e) table[i] = next; else prev.next = next; // 注意这里, 在LinkedHashMap中, 还需要维护输入顺序的链表, 所以Entry.recordRemoval, 需要做些什么.. e.recordRemoval(this); return e; } prev = e; e = next; } return e;}Entry. recordRemoval(HashMap<K,V> m) void recordRemoval(HashMap<K,V> m) { remove(); }Entry. remove() // 在输入顺序链表中移除当前entry private void remove() { before.after = after; after.before = before; }
Entry[InnerClass]
private static class Entry<K,V> extends HashMap.Entry<K,V> { // 继承自HashMap.Entry 添加两个引用用于保存插入元素的顺序[通过这两者遍历] // 并且重写了更新元素之后的回调, 删除元素之后的回调 // These fields comprise the doubly linked list used for iteration. Entry<K,V> before, after; Entry(int hash, K key, V value, HashMap.Entry<K,V> next) { super(hash, key, value, next); } // 在输入顺序链表中移除当前entry 由recordRemoval[HashMap.remove(obj)], recordAccess调用 private void remove() { before.after = after; after.before = before; } // 在existingEntry前 添加本entry private void addBefore(Entry<K,V> existingEntry) { after = existingEntry; before = existingEntry.before; before.after = this; after.before = this; } // 在Map.get/set 方法中被调用 void recordAccess(HashMap<K,V> m) { LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m; if (lm.accessOrder) { lm.modCount++; remove(); addBefore(lm.header); } } // 在Map. removeMapping/ removeEntryForKey (Object o)中被调用 void recordRemoval(HashMap<K,V> m) { remove(); } }
LinkedHashIterator[InnerClass]
private abstract class LinkedHashIterator<T> implements Iterator<T> { // 初始的的nextEntry为header.after, 然后沿着这条输入顺序链表走下去 Entry<K,V> nextEntry = header.after; Entry<K,V> lastReturned = null; /** * The modCount value that the iterator believes that the backing * List should have. If this expectation is violated, the iterator * has detected concurrent modification. */ int expectedModCount = modCount; // 判断是否存在下一个元素的依据就是 nextEntry是否为header 因为after和before将所有的元素组成了一个双向链表 public boolean hasNext() { return nextEntry != header; } // 移除本entry // 调用的是HashMap.remove(key) // 其中调用了e.recordRemoval(this); 清除该entry相应的after,before关系 public void remove() { if (lastReturned == null) throw new IllegalStateException(); if (modCount != expectedModCount) throw new ConcurrentModificationException(); LinkedHashMap.this.remove(lastReturned.key); lastReturned = null; expectedModCount = modCount; } // 并发修改检查 // 保留一个nextEntry的引用用于返回 然后直接将nextEntry 赋值为nextEntry.after Entry<K,V> nextEntry() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); if (nextEntry == header) throw new NoSuchElementException(); Entry<K,V> e = lastReturned = nextEntry; nextEntry = e.after; return e; } }
EntryIterator[InnerClass]
private class EntryIterator extends LinkedHashIterator<Map.Entry<K,V>> { public Map.Entry<K,V> next() { return nextEntry(); } }
KeyIterator[InnerClass]
private class KeyIterator extends LinkedHashIterator<K> { public K next() { return nextEntry().getKey(); } }
ValueIterator[InnerClass]
private class ValueIterator extends LinkedHashIterator<V> { public V next() { return nextEntry().value; } }
LinkedHashMap. new Entry/ Key/ Value Iterator()
// 重写了下面的三个方法, 使其应用LinkedHashMap的相应的迭代器 // 用于HashMap中的方法多态调用 keyset,valueSet,entrySet方法// These Overrides alter the behavior of superclass view iterator() methods Iterator<Map.Entry<K,V>> newEntryIterator() { return new EntryIterator(); } Iterator<K> newKeyIterator() { return new KeyIterator(); } Iterator<V> newValueIterator() { return new ValueIterator(); }
-> end
LinkedHashMap在拥有HashMap的查找性能的同时, 还维护了顺序, 难点 主要在于区分Entry结点的三部分数据, 数据域, 链表结点的next域, 维护顺序的before/ after域
以及在添加元素, 或者删除元素的时候 : next指针域维护链表的性质, before/ after指针域维护顺序的链表的性质
还有一个是关于迭代数据的优化, 使用维护的顺序链表进行迭代
java.util.LinkedHashSet 和LinkedHashMap : 前者继承自HashSet 并且基于后者的 [因为初始化的时候 调用了超类的构造方法[该构造方式是包权限的, 外部不能调用], 创建了一个LinkedHashMap的实例], LinkedHashSet中维护了一个LinkedHashMap的实例, 该HashMap的每一个Entry< key, value > 中value均为 初始化HashSet.class 的时候创建的一个PRESENT[new Object()]
使用该LinkedHashMap的所有的key来存储数据 [利用了LinkedHashMap的key唯一性保证了LinkedHashSet中元素的唯一性]
- 14 java.util.LinkedHashMap
- java.util包---LinkedHashMap
- java.util.LinkedHashMap源码解析
- java.util(二)HashMap TreeMap LinkedHashMap
- java.util.LinkedHashMap cannot be cast to
- java.util.ConcurrentModificationException at java.util.LinkedHashMap$LinkedHashIterator.nextEntry(L
- java.util.LinkedHashMap.eldest()Ljava/util/Map$Entry 解决办法
- mybatis中select查询时 resultType="java.util.LinkedHashMap"
- An LRU cache class based on java.util.LinkedHashMap
- 使用 Java.util.LinkedHashMap 实现 LRU、FIFO 算法
- java.util.LinkedHashMap cannot be cast to com.XXX.XXX
- android UI显示java.util.LinkedHashMap.eldest()Ljava/util/Map$Entry;E
- Java LinkedHashMap
- 【exceptions】java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to xxxxx
- Jackson转换泛型List出现错误java.util.LinkedHashMap cannot be cast to com.xxx
- Failed to convert property value of type 'java.util.LinkedHashMap' to required
- Spring Web's RestTemplate, exchange. java.util.LinkedHashMap cannot be cast to
- Jackson转换泛型List出现错误java.util.LinkedHashMap cannot be cast to com.xxx
- Linux 安装svn
- Android:控件AutoCompleteTextView 客户端保存搜索历史自动提示
- 【交换安全】交换机端口安全Port-Security超级详解
- 测试数学公式的网页显示
- hdu1076 An Easy Task
- 14 java.util.LinkedHashMap
- ofstream和ifstream详细用法
- 惠普第一阶段学习总结
- Android篇---Styles和Themes常见用法可能疑小结
- DHCP snooping详解
- spark on machine learning--基本统计
- hdu 2457(ac自动机+dp)
- 【交换安全】DAI - Dynamic ARP Inspection 详解
- HBuilder开发App