用LinkedHashMap实现FIFO、LRU

104 篇文章 0 订阅
6 篇文章 0 订阅

用LinkedHashMap实现FIFO、LRU

来源:http://www.xymyeah.com/272.html

HashMap与LinkedHashMap最大的不同在于,后者维护者一个运行于所有条目的双向链表。有了这个双向链表,就可以在迭代的时候按照插入的顺序迭代出元素(当然也可以通过LRU算法迭代元素,下面会讲到)

 

1. 类结构

Java代码   收藏代码
  1. public class LinkedHashMap<K, V> extends HashMap<K, V> implements Map<K, V>  

 

我们可以看出LinkedHashMap继承了HashMap!

 

2. 几个重要的成员变量

Java代码   收藏代码
  1. /**                                                                      
  2.  * The head of the doubly linked list.                                   
  3.  */                                                                       
  4. private transient Entry<K, V> header;                                     
  5.                                                                           
  6. /**                                                                      
  7.  * The iteration ordering method for this linked hash map: <tt>true</tt> 
  8.  * for access-order, <tt>false</tt> for insertion-order.                 
  9.  *                                                                       
  10.  * @serial                                                               
  11.  */                                                                       
  12. private final boolean accessOrder;                                        

 

 对于父类HashMap中的成员变量这里就不讲了,可参考http://boy00fly.iteye.com/blog/1139845

 其中Entry类是LinkedHashMap的内部类定义,代码片段如下:

Java代码   收藏代码
  1. //继承了HashMap.Entry                                         
  2. private static class Entry<K, V> extends HashMap.Entry<K, V>  
  3. //新增的两个成员变量                                          
  4. Entry<K, V> before, after;                                    

 

 可以看得出来新增的这两个变量就是双向链表实现的关键!!分别指向当前Entry的前一个、后一个Entry。

 

header就是指向双向链表的头部!

accessOrder:true则按照LRU算法迭代整个LinkedHashMap,false则按照元素的插入顺序迭代!

 

3. 几个重要的构造函数

Java代码   收藏代码
  1. /** 
  2.   * Constructs an empty <tt>LinkedHashMap</tt> instance with the 
  3.   * specified initial capacity, load factor and ordering mode. 
  4.   * 
  5.   * @param  initialCapacity the initial capacity 
  6.   * @param  loadFactor      the load factor 
  7.   * @param  accessOrder     the ordering mode - <tt>true</tt> for 
  8.   *         access-order, <tt>false</tt> for insertion-order 
  9.   * @throws IllegalArgumentException if the initial capacity is negative 
  10.   *         or the load factor is nonpositive 
  11.   */  
  12.  public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder)  
  13.  {  
  14.      super(initialCapacity, loadFactor);  
  15.      this.accessOrder = accessOrder;  
  16.  }  

    是不是很简单,也可参考http://boy00fly.iteye.com/blog/1139845这篇文章的构造函数分析。其中accessOrder就是我们上面所讲的控制迭代顺序的开关,只有此构造函数有这个参数,其他的构造函数默认就是false。

 

4. 几个重要的方法

Java代码   收藏代码
  1. /** 
  2.   * Called by superclass constructors and pseudoconstructors (clone, 
  3.   * readObject) before any entries are inserted into the map.  Initializes 
  4.   * the chain. 
  5.   */  
  6.  void init()  
  7.  {  
  8.      header = new Entry<K, V>(-1nullnullnull);  
  9.      header.before = header.after = header;  
  10.  }  

 此方法是在父类构造函数初始化的时候调用的,LinkedHashMap重写了init方法。代码中表达的意思也很明确了,这是双向链表的初始化状态。

 

 

 

Java代码   收藏代码
  1. /**                                                                         
  2.  * This override alters behavior of superclass put method. It causes newly  
  3.  * allocated entry to get inserted at the end of the linked list and        
  4.  * removes the eldest entry if appropriate.                                 
  5.  */                                                                          
  6. void addEntry(int hash, K key, V value, int bucketIndex)                     
  7. {                                                                            
  8.     createEntry(hash, key, value, bucketIndex);                              
  9.                                                                              
  10.     // Remove eldest entry if instructed, else grow capacity if appropriate  
  11.     Entry<K, V> eldest = header.after;                                       
  12.     if (removeEldestEntry(eldest))                                           
  13.     {                                                                        
  14.         removeEntryForKey(eldest.key);                                       
  15.     }                                                                        
  16.     else                                                                     
  17.     {                                                                        
  18.         if (size >= threshold)                                               
  19.             resize(2 * table.length);                                        
  20.     }                                                                        
  21. }                                                                            
  22.   
  23. /**                                                                  
  24.  * This override differs from addEntry in that it doesn't resize the 
  25.  * table or remove the eldest entry.                                 
  26.  */                                                                   
  27. void createEntry(int hash, K key, V value, int bucketIndex)           
  28. {                                                                     
  29.     HashMap.Entry<K, V> old = table[bucketIndex];                     
  30.     Entry<K, V> e = new Entry<K, V>(hash, key, value, old);           
  31.     table[bucketIndex] = e;                                           
  32.     e.addBefore(header);                                              
  33.     size++;                                                           
  34. }                                                                     
  35.   
  36. /**                                                                    
  37.  * Inserts this entry before the specified existing entry in the list. 
  38.  */                                                                     
  39. private void addBefore(Entry<K, V> existingEntry)                       
  40. {                                                                       
  41.     after = existingEntry;                                              
  42.     before = existingEntry.before;                                      
  43.     before.after = this;                                                
  44.     after.before = this;                                                
  45. }                                                                       

 

 addEntry方法是父类HashMap的一个方法,被put相关的方法所调用即在新增元素的时候调用。

 我们通过形象的图来看一个基本的流程:

(从初始化到添加了3个元素过程中,各个元素before、after引用变化,画的有点丑,呵呵,不过意思能够表达清楚,代码的内容就不再累述了,大体和HashMap类似!)



 

 

 

再介绍一个get方法

 

Java代码   收藏代码
  1. /**                                                                       
  2.  * Returns the value to which the specified key is mapped,                
  3.  * or {@code null} if this map contains no mapping for the key.           
  4.  *                                                                        
  5.  * <p>More formally, if this map contains a mapping from a key            
  6.  * {@code k} to a value {@code v} such that {@code (key==null ? k==null : 
  7.  * key.equals(k))}, then this method returns {@code v}; otherwise         
  8.  * it returns {@code null}.  (There can be at most one such mapping.)     
  9.  *                                                                        
  10.  * <p>A return value of {@code null} does not <i>necessarily</i>          
  11.  * indicate that the map contains no mapping for the key; it's also       
  12.  * possible that the map explicitly maps the key to {@code null}.         
  13.  * The {@link #containsKey containsKey} operation may be used to          
  14.  * distinguish these two cases.                                           
  15.  */                                                                        
  16. public V get(Object key)                                                   
  17. {                                                                          
  18.     Entry<K, V> e = (Entry<K, V>)getEntry(key);                            
  19.     if (e == null)                                                         
  20.         return null;                                                       
  21.     e.recordAccess(this);                                                  
  22.     return e.value;                                                        
  23. }                        
  24.   
  25. /**                                                                    
  26.   * This method is invoked by the superclass whenever the value        
  27.   * of a pre-existing entry is read by Map.get or modified by Map.set. 
  28.   * If the enclosing Map is access-ordered, it moves the entry         
  29.   * to the end of the list; otherwise, it does nothing.                
  30.   */                                                                    
  31.  void recordAccess(HashMap<K, V> m)                                     
  32.  {                                                                      
  33.      LinkedHashMap<K, V> lm = (LinkedHashMap<K, V>)m;                   
  34.      if (lm.accessOrder)                                                
  35.      {                                                                  
  36.          lm.modCount++;                                                 
  37.          remove();                                                      
  38.          addBefore(lm.header);                                          
  39.      }                                                                  
  40.  }           
  41.   
  42. /**                                         
  43.  * Removes this entry from the linked list. 
  44.  */                                          
  45. private void remove()                        
  46. {                                            
  47.     before.after = after;                    
  48.     after.before = before;                   
  49. }                                                                                                   

 

 这里我们来看一下recordAccess方法!

上面我们不是讲过accessOrder这个参数值控制着LinkedHashMap的迭代顺序嘛,这里我们来看一下。

 当accessOrder为true时,remove方法就是将当前元素从双向链表中移除,

addBefore方法再将当前元素插入到链表的头部去,这样最近读到的元素,在迭代的时候是优先被迭代出来的!

这就是所谓的LRU算法(Least Recently Used):最近最少使用算法。

当accessOrder为false时,不做任何事情,就按照插入顺序迭代出来!

 

http://www.xymyeah.com/272.html



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值