集合框架源码分析五之LinkedHashMap,LinkedHashSet

来源:互联网 发布:京东商城javaweb源码 编辑:程序博客网 时间:2024/06/08 11:53
   LinkedHashMap是为了解决遍历Hash表的无序问题,它内部维护了一个链表用于记录你插入元素(或你访问元素的顺序)的位置,遍历时直接遍历链表,元素的顺序即为你插入的顺序,但是Entry对象要多加两个成员变量before和after用于记录链表的前驱和后继。所以LinkedHashMap的的存储效率要低于HashMap,但是遍历效率要高于HashMap。
java.util.LinkedHashMap
Java代码  收藏代码
  1. import java.util.ConcurrentModificationException;  
  2. import java.util.HashMap;  
  3. import java.util.Iterator;  
  4. import java.util.Map;  
  5. import java.util.NoSuchElementException;  
  6.   
  7. /** 
  8.  * LinkedHashMap内部不仅存储了一个散列表, 
  9.  * 也维护了一个链表, 
  10.  * 根据链表中元素的顺序可以分为:按插入顺序的链表,和按访问顺序(调用get方法)的链表。 
  11.  * 默认是按插入顺序排序,如果指定按访问顺序排序,那么调用get方法后,会将这次访问的元素 
  12.  * 移至链表尾部,不断访问可以形成按访问顺序排序的链表。 
  13.  * 可以重写removeEldestEntry方法返回true值指定插入元素时移除最老的元素 
  14.  */  
  15. public class LinkedHashMap<K,V> extends HashMap<K,V>  
  16.     implements Map<K,V>  
  17. {  
  18.   
  19.     private static final long serialVersionUID = 3801124242820219131L;  
  20.   
  21.     /** 
  22.      * 此Map中维护的一个双向循环链表的头节点 
  23.      */  
  24.     private transient Entry<K,V> header;  
  25.   
  26.     /** 
  27.      *  
  28.      * 遍历Map的顺序的变量, 
  29.      * true为按访问顺序遍历 
  30.      * false为按插入顺序遍历 
  31.      */  
  32.     private final boolean accessOrder;  
  33.   
  34.     /** 
  35.      *  
  36.      * 构造一个空的,按插入顺序遍历的LinkedHashMap实例, 
  37.      * 初始容量与加载因子由自己指定 
  38.      */  
  39.     public LinkedHashMap(int initialCapacity, float loadFactor) {  
  40.         super(initialCapacity, loadFactor);  
  41.         accessOrder = false;  
  42.     }  
  43.   
  44.     /** 
  45.      * 构造一个空的,按插入顺序遍历的LinkedHashMap实例, 
  46.      * 初始容量由自己指定 
  47.      */  
  48.     public LinkedHashMap(int initialCapacity) {  
  49.         super(initialCapacity);  
  50.         accessOrder = false;  
  51.     }  
  52.   
  53.     /** 
  54.      *  
  55.      * 构造一个空的,按插入顺序遍历的LinkedHashMap实例 
  56.      * 初始容量与加载因子按HashMap的默认值16与0.75 
  57.      */  
  58.     public LinkedHashMap() {  
  59.         super();  
  60.         accessOrder = false;  
  61.     }  
  62.   
  63.     /** 
  64.      * 构造一个非空,按插入顺序遍历的LinkedHashMap实例 
  65.      */  
  66.     public LinkedHashMap(Map<? extends K, ? extends V> m) {  
  67.         super(m);  
  68.         accessOrder = false;  
  69.     }  
  70.   
  71.     public LinkedHashMap(int initialCapacity,  
  72.                          float loadFactor,  
  73.                          boolean accessOrder) {  
  74.         super(initialCapacity, loadFactor);  
  75.         this.accessOrder = accessOrder;  
  76.     }  
  77.       
  78.     /** 
  79.      *  
  80.      * 在HashMap每个构造方法中都会调用一次此init()方法 
  81.      * 这里是初始化一个双向循环链表的头节点 
  82.      */  
  83.     void init() {  
  84.         header = new Entry<K,V>(-1nullnullnull);  
  85.         header.before = header.after = header;  
  86.     }  
  87.   
  88.     /** 
  89.      *  
  90.      * 在父类HashMap进行容量扩充时会调用此方法 
  91.      * 把原来Entry数组中的对象经过重新计算索引转移到此newTable中 
  92.      * 这里与HashMap中的transfer方法的实现不再相同,而且比它更快。 
  93.      *  
  94.      * HashMap是遍历Entry数组,需要检查是否为null,如果不为空的话则就需 
  95.      * 要遍历数组中的链表,而这里所有的元素都链接成了一个链表,直接遍历此 
  96.      * 链表就可以遍历出LinkedHashMap中的所有元素。 
  97.      */  
  98.     void transfer(HashMap.Entry[] newTable) {  
  99.         int newCapacity = newTable.length;  
  100.         for (Entry<K,V> e = header.after; e != header; e = e.after) {  
  101.             int index = indexFor(e.hash, newCapacity);   //根据新容量重新计算index  
  102.             e.next = newTable[index];   //最后添加的节点放最前面  
  103.             newTable[index] = e;  
  104.         }  
  105.     }  
  106.   
  107.   
  108.     /** 
  109.      *  
  110.      *  查找Map中是否含有本值,这里重写了HashMap的containsValue方法 
  111.      *  因为在LinkedHashMap中只需要遍历链表查找,这比HashMap遍历table查找更加 
  112.      *  高效和快速 
  113.      */  
  114.     public boolean containsValue(Object value) {  
  115.         // Overridden to take advantage of faster iterator  
  116.         if (value==null) {  
  117.             for (Entry e = header.after; e != header; e = e.after)  
  118.                 if (e.value==null)  
  119.                     return true;  
  120.         } else {  
  121.             for (Entry e = header.after; e != header; e = e.after)  
  122.                 if (value.equals(e.value))  
  123.                     return true;  
  124.         }  
  125.         return false;  
  126.     }  
  127.   
  128.     /** 
  129.      *  
  130.      * 通过key值获得value,如果没有找到此key则返回null。 
  131.      * 不过返回null也可能是其value为null 
  132.      * 通过contain方法可判断Map中是否含有此键 
  133.      *  
  134.      * 重写此方法的原因是: 
  135.      * 如果map中链表是按照访问顺序进行排序, 
  136.      * 就需要把本次访问的元素移到链表尾部,表示 
  137.      * 这是你最新访问的元素,以后可能会继续访问它 
  138.      *  
  139.      */  
  140.     public V get(Object key) {  
  141.         Entry<K,V> e = (Entry<K,V>)getEntry(key);  
  142.         if (e == null)  
  143.             return null;  
  144.         e.recordAccess(this);  
  145.         return e.value;  
  146.     }  
  147.   
  148.     /** 
  149.      * 移除元素,清空链表 
  150.      */  
  151.     public void clear() {  
  152.         super.clear();  
  153.         header.before = header.after = header;  
  154.     }  
  155.   
  156.     /** 
  157.      * LinkedHashMap 的Entry对象. 
  158.      */  
  159.     private static class Entry<K,V> extends HashMap.Entry<K,V> {  
  160.         // 包含其前驱和后继的信息.  
  161.         Entry<K,V> before, after;  
  162.   
  163.         Entry(int hash, K key, V value, HashMap.Entry<K,V> next) {  
  164.             super(hash, key, value, next);  
  165.         }  
  166.   
  167.         /** 
  168.          *  
  169.          * 在链表中移除此对象只需要修改前驱和后继的指针 
  170.          */  
  171.         private void remove() {  
  172.             before.after = after;  
  173.             after.before = before;  
  174.         }  
  175.   
  176.         /** 
  177.          *  
  178.          * 把元素插入到指定的存在的元素之前 
  179.          * 链表的插入也是修改指针 
  180.          */  
  181.         private void addBefore(Entry<K,V> existingEntry) {  
  182.             after  = existingEntry;  
  183.             before = existingEntry.before;  
  184.             before.after = this;  
  185.             after.before = this;  
  186.         }  
  187.   
  188.         /** 
  189.          *  
  190.          * 当调用父类的put或putAll方法,发现要插入的键已经存在时会调用此方法, 
  191.          * 当调用LinkedHashMap的get方法时会调用此方法。 
  192.          * 如果LinkedHashMap是按访问顺序遍历的,就移动此Entry 
  193.          * 到链表的最后位置,否则do nothing 
  194.          */  
  195.         void recordAccess(HashMap<K,V> m) {  
  196.             LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;  
  197.             if (lm.accessOrder) {  
  198.                 lm.modCount++;  
  199.                 remove();       //移除它  
  200.                 addBefore(lm.header);   //再把她添加到链表尾部  
  201.             }  
  202.         }  
  203.   
  204.         /** 
  205.          * 在移除元素时会调用此方法,即调用 
  206.          * 父类的remove(key)方法时调用它 
  207.          * 这里是改变链表指针,移除链表中的此元素 
  208.          */  
  209.         void recordRemoval(HashMap<K,V> m) {  
  210.             remove();  
  211.         }  
  212.     }  
  213.   
  214.     private abstract class LinkedHashIterator<T> implements Iterator<T> {  
  215.         Entry<K,V> nextEntry    = header.after;       //初始为第一个元素  
  216.         Entry<K,V> lastReturned = null;  
  217.   
  218.         /** 
  219.          * 检测同步 
  220.          */  
  221.         int expectedModCount = modCount;  
  222.   
  223.         public boolean hasNext() {  
  224.                 return nextEntry != header;  
  225.         }  
  226.   
  227.         public void remove() {  
  228.             if (lastReturned == null)  
  229.                 throw new IllegalStateException();  
  230.             if (modCount != expectedModCount)  
  231.                 throw new ConcurrentModificationException();  
  232.       
  233.             LinkedHashMap.this.remove(lastReturned.key);  
  234.             lastReturned = null;  
  235.             expectedModCount = modCount;  
  236.         }  
  237.   
  238.         Entry<K,V> nextEntry() {  
  239.             if (modCount != expectedModCount)  
  240.                 throw new ConcurrentModificationException();  
  241.             if (nextEntry == header)  
  242.                 throw new NoSuchElementException();  
  243.       
  244.             Entry<K,V> e = lastReturned = nextEntry;  
  245.             nextEntry = e.after;  
  246.             return e;  
  247.         }  
  248.     }  
  249.   
  250.     //依次重写next方法,返回不同的迭代器。  
  251.     private class KeyIterator extends LinkedHashIterator<K> {  
  252.         public K next() { return nextEntry().getKey(); }  
  253.     }  
  254.   
  255.     private class ValueIterator extends LinkedHashIterator<V> {  
  256.         public V next() { return nextEntry().value; }  
  257.     }  
  258.   
  259.     private class EntryIterator extends LinkedHashIterator<Map.Entry<K,V>> {  
  260.         public Map.Entry<K,V> next() { return nextEntry(); }  
  261.     }  
  262.   
  263.     // These Overrides alter the behavior of superclass view iterator() methods  
  264.     Iterator<K> newKeyIterator()   { return new KeyIterator();   }  
  265.     Iterator<V> newValueIterator() { return new ValueIterator(); }  
  266.     Iterator<Map.Entry<K,V>> newEntryIterator() { return new EntryIterator(); }  
  267.   
  268.     /** 
  269.      *  
  270.      * 重写父类addEntry方法 
  271.      */  
  272.     void addEntry(int hash, K key, V value, int bucketIndex) {  
  273.         createEntry(hash, key, value, bucketIndex);  
  274.   
  275.           
  276.         /*如果子类重写了removeEldestEntry方法并返回true, 
  277.                           将在map中移除链表中的第一个元素,否则检测是否需要扩充容量。 
  278.           eldest元素即为链表中的第一个元素*/  
  279.         Entry<K,V> eldest = header.after;  
  280.         if (removeEldestEntry(eldest)) {  
  281.             removeEntryForKey(eldest.key);  
  282.         } else {  
  283.             if (size >= threshold)  
  284.                 resize(2 * table.length);   //扩充至原来的2倍  
  285.         }  
  286.     }  
  287.   
  288.     /** 
  289.      *  
  290.      * 重写父类的createEntry方法,此方法不需要检测是否要扩充容量, 
  291.      * 与HashMap中此方法的唯一区别时,这里不仅把元素插入到散列表中, 
  292.      * 而且将元素插入到了链表尾 
  293.      */  
  294.     void createEntry(int hash, K key, V value, int bucketIndex) {  
  295.         HashMap.Entry<K,V> old = table[bucketIndex];  
  296.         Entry<K,V> e = new Entry<K,V>(hash, key, value, old);  
  297.         table[bucketIndex] = e;  
  298.         e.addBefore(header);  
  299.         size++;  
  300.     }  
  301.   
  302.     /** 
  303.      *  
  304.      * 此方法会在调用put()或putAll()方法时调用,此方法告诉linkedHashMap 
  305.      * 是否在添加一个元素后移除最老的元素 
  306.      * 比如下面代码会让LinedHashMap的大小始终保持在100 
  307.      *     private static final int MAX_ENTRIES = 100; 
  308.      * 
  309.      *     protected boolean removeEldestEntry(Map.Entry eldest) { 
  310.      *        return size() > MAX_ENTRIES; 
  311.      *     } 
  312.      *  此方法默认返回false,需要子类去复写此方法 
  313.      *   
  314.      *  什么是eldest元素? 
  315.      *  如果按插入问顺序来说,是map中最先插入的元素 
  316.      *  如果按访问顺序来说,是map中最久未被访问的元素 
  317.      */  
  318.     protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {  
  319.         return false;  
  320.     }  
  321. }  



关于LinkedHashSet
Java代码  收藏代码
  1. /** 
  2.  * LinkedHashSet实际上是基于LinkedHashMap的基础上实现的, 
  3.  * LinkedHashSet继承自HashSet,在HashSet中有一构造方法 
  4.  * HashSet(int initialCapacity, float loadFactor, boolean dummy)  
  5.  * 第三个参数dummy为true时new出了一个LinkedHashMap实例,以Set中的 
  6.  * 元素为键,以一个Object的虚假的对象为值,所以HashSet中的元素不可能重复。 
  7.  * 以下构造函数dummy都为true 
  8.  */  
  9. public class LinkedHashSet<E>  
  10. extends HashSet<E>  
  11. implements Set<E>, Cloneable, java.io.Serializable {  
  12.   
  13. private static final long serialVersionUID = -2851667679971038690L;  
  14.   
  15.   
  16. public LinkedHashSet(int initialCapacity, float loadFactor) {  
  17.     super(initialCapacity, loadFactor, true);  
  18. }  
  19.   
  20.   
  21. public LinkedHashSet(int initialCapacity) {  
  22.     super(initialCapacity, .75f, true);  
  23. }  
  24.   
  25.   
  26. public LinkedHashSet() {  
  27.     super(16, .75f, true);  
  28. }  
  29.   
  30.   
  31. public LinkedHashSet(Collection<? extends E> c) {  
  32.     super(Math.max(2*c.size(), 11), .75f, true);  
  33.     addAll(c);  
  34. }  

原创粉丝点击