Map---HashMap-LinkedHashMap
概述
LinkedHashMap是HashMap的直接子类;
二者唯一的区别是LinkedHashMap在HashMap的基础上,采用双向链表(doubly-linked list)的形式将所有entry
连接起来,这样是为保证元素的迭代顺序跟插入顺序相同。
上图给出了LinkedHashMap的结构图,主体部分跟HashMap完全一样,多了header
指向双向链表的头部(是一个哑元),该双向链表的迭代顺序就是entry
的插入顺序。
除了可以保迭代历顺序,这种结构还有一个好处 : 迭代LinkedHashMap时不需要像HashMap那样遍历整个table
,而只需要直接遍历header
指向的双向链表即可,
也就是说LinkedHashMap的迭代时间就只跟entry
的个数相关,而跟table
的大小无关。
有两个参数可以影响LinkedHashMap的性能: 初始容量(inital capacity)和负载系数(load factor)。
初始容量指定了初始table
的大小,负载系数用来指定自动扩容的临界值。
当entry
的数量超过capacity*load_factor
时,容器将自动扩容并重新哈希。对于插入元素较多的场景,将初始容量设大可以减少重新哈希的次数。
将对象放入到LinkedHashMap或LinkedHashSet中时,有两个方法需要特别关心: hashCode()
和equals()
。
hashCode()
方法决定了对象会被放到哪个bucket
里,当多个对象的哈希值冲突时,equals()
方法决定了这些对象是否是“同一个对象”。
所以,如果要将自定义的对象放入到LinkedHashMap
或LinkedHashSet
中,需要@Override hashCode()
和equals()
方法。
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.
Map接口的hash表+链表实现(有可预测的iterator顺序);
LinkedHashMap是双向链表;
This linked list defines the iteration ordering, which is normally the order in which keys were inserted into the map (<i>insertion-order</i>).
定义了iterator顺序,一般是 插入顺序;
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.)
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>).
提供了一个 LinkedHashMap(int,float,boolean)构造器 创建一个linked hash map
This class provides all of the optional <tt>Map</tt> operations, and permits null elements.
LinkedHashMap允许null值;
A linked hash map has two parameters that affect its performance:<i>initial capacity</i> and <i>load factor</i>.
LinkedHashMap有2个影响性能的参数: initial capacity、load factor
Note that this implementation is not synchronized.
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.
If no such object exists, the map should be "wrapped" using the {@link Collections#synchronizedMap Collections.synchronizedMap} method.
LinkedHashMap是线程非同步的;
如果多个线程并发访问LinkedHashMap,需要在外部同步;
同步方式:Collections.synchronizedMap;
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}.
iterator方法是fail-fast:如果在iterator的同时对结构进行修改,将会抛出ConcurrentModificationException;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V> { static class Entry<K,V> extends HashMap.Node<K,V> { LinkedHashMap.Entry<K,V> before, after; Entry( int hash, K key, V value, Node<K,V> next) { super (hash, key, value, next); } } /** * The head (eldest) of the doubly linked list. */ transient LinkedHashMap.Entry<K,V> head; /** * The tail (youngest) of the doubly linked list. */ transient LinkedHashMap.Entry<K,V> tail; // The iteration ordering method for this linked hash map: true for access-order, false for insertion-order. // 迭代排序:true->access-order、false->insertion-order final boolean accessOrder; // Constructs an empty insertion-ordered <tt>LinkedHashMap</tt> instance with the default initial capacity (16) and load factor (0.75). public LinkedHashMap() { super (); accessOrder = false ; } public LinkedHashMap( int initialCapacity, float loadFactor, boolean accessOrder) { super (initialCapacity, loadFactor); this .accessOrder = accessOrder; } } |
LinkedHashMap的扩展
accessOrder
构造参数accessOrder为true,get时会将该元素move到队尾;
1 2 3 4 5 6 7 8 9 10 11 12 | LinkedHashMap<String, String> linkedHashMap = new LinkedHashMap<>( 10 , 0 .75F, true ); linkedHashMap.put( "a" , "a" ); linkedHashMap.put( "b" , "b" ); linkedHashMap.put( "c" , "c" ); linkedHashMap.put( "d" , "d" ); System.out.println(linkedHashMap.toString()); // {a=a, b=b, c=c, d=d} linkedHashMap.get( "c" ); System.out.println(linkedHashMap.toString()); // {a=a, b=b, d=d, c=c} linkedHashMap.entrySet().stream().forEach(e -> System.out.print( " " +e)); // a=a b=b d=d c=c |
removeEldestEntry
LinkedHashMap每次插入新的元素,都会调用removeEldestEntry方法,如果返回true,移除最老的元素;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | static class FIFOCache<K, V> extends LinkedHashMap<K, V>{ private final int cacheSize; public FIFOCache( int cacheSize){ this .cacheSize = cacheSize; } // 当Entry个数超过cacheSize时,删除最老的Entry @Override protected boolean removeEldestEntry(Map.Entry<K,V> eldest) { return size() > cacheSize; } } FIFOCache<String, String> fifoCache = new FIFOCache<>( 4 ); fifoCache.put( "a" , "a" ); fifoCache.put( "b" , "b" ); fifoCache.put( "c" , "c" ); fifoCache.put( "d" , "d" ); fifoCache.put( "e" , "e" ); System.out.print(fifoCache); // {b=b, c=c, d=d, e=e} |
链路
Put
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | // java.util.HashMap.put public V put(K key, V value) { return putVal(hash(key), key, value, false , true ); } // java.util.HashMap.putVal final V putVal( int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node<K,V>[] tab; Node<K,V> p; int n, i; if ((tab = table) == null || (n = tab.length) == 0 ) n = (tab = resize()).length; if ((p = tab[i = (n - 1 ) & hash]) == null ) tab[i] = newNode(hash, key, value, null ); // 创建新的node && link到列表尾部 else { Node<K,V> e; K k; if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; else if (p instanceof TreeNode) e = ((TreeNode<K,V>)p).putTreeVal( this , tab, hash, key, value); else { for ( int binCount = 0 ; ; ++binCount) { if ((e = p.next) == null ) { p.next = newNode(hash, key, value, null ); if (binCount >= TREEIFY_THRESHOLD - 1 ) // -1 for 1st treeifyBin(tab, hash); break ; } if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break ; p = e; } } if (e != null ) { // existing mapping for key V oldValue = e.value; if (!onlyIfAbsent || oldValue == null ) e.value = value; afterNodeAccess(e); return oldValue; } } ++modCount; if (++size > threshold) resize(); afterNodeInsertion(evict); return null ; } // java.util.LinkedHashMap.newNode HashMap.Node<K,V> newNode( int hash, K key, V value, HashMap.Node<K,V> e) { LinkedHashMap.Entry<K,V> p = new LinkedHashMap.Entry<K,V>(hash, key, value, e); linkNodeLast(p); return p; } // java.util.LinkedHashMap.linkNodeLast private void linkNodeLast(LinkedHashMap.Entry<K,V> p) { // link到列表尾部 LinkedHashMap.Entry<K,V> last = tail; tail = p; if (last == null ) head = p; else { p.before = last; last.after = p; } } // java.util.LinkedHashMap.afterNodeInsertion void afterNodeInsertion( boolean evict) { // possibly remove eldest LinkedHashMap.Entry<K,V> first; if (evict && (first = head) != null && removeEldestEntry(first)) { // removeEldestEntry每次Put都要看下是否需要移除最老的元素 K key = first.key; removeNode(hash(key), key, null , false , true ); } } |
get
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | // java.util.LinkedHashMap.get public V get(Object key) { HashMap.Node<K,V> e; if ((e = getNode(hash(key), key)) == null ) return null ; if (accessOrder) afterNodeAccess(e); // 如果accessOrder=true -> 将访问的元素插入尾部 return e.value; } // java.util.LinkedHashMap.afterNodeAccess void afterNodeAccess(HashMap.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; } } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)