zoukankan      html  css  js  c++  java
  • LinkedHashMap 阅读笔记

    之前看了 HashMap 的源码,看 LinkedHashMap 要轻松不少。

    先看数据的储存形式:

        /**
         * LinkedEntry adds nxt/prv double-links to plain HashMapEntry.
         */
        static class LinkedEntry<K, V> extends HashMapEntry<K, V> {
            LinkedEntry<K, V> nxt;
            LinkedEntry<K, V> prv;
    
            /** Create the header entry */
            LinkedEntry() {
                super(null, null, 0, null);
                nxt = prv = this;
            }
    
            /** Create a normal entry */
            LinkedEntry(K key, V value, int hash, HashMapEntry<K, V> next,
                        LinkedEntry<K, V> nxt, LinkedEntry<K, V> prv) {
                super(key, value, hash, next);
                this.nxt = nxt;
                this.prv = prv;
            }
        }
    

    看到 pre 和 nxt 就知道这是一个双向链表结构。

    构造函数里面:nxt = prv = this; 说明这还是一个环形双向链表。

    根据 HashMap 的源码,我们调用了 put 方法之后,put 方法会调用 addNewEntry 方法(如果 key 值不存在)。

        /**
         * Evicts eldest entry if instructed, creates a new entry and links it in
         * as head of linked list. This method should call constructorNewEntry
         * (instead of duplicating code) if the performance of your VM permits.
         *
         * <p>It may seem strange that this method is tasked with adding the entry
         * to the hash table (which is properly the province of our superclass).
         * The alternative of passing the "next" link in to this method and
         * returning the newly created element does not work! If we remove an
         * (eldest) entry that happens to be the first entry in the same bucket
         * as the newly created entry, the "next" link would become invalid, and
         * the resulting hash table corrupt.
         */
        @Override void addNewEntry(K key, V value, int hash, int index) {
            LinkedEntry<K, V> header = this.header;
    
            // Remove eldest entry if instructed to do so.
            LinkedEntry<K, V> eldest = header.nxt;
            if (eldest != header && removeEldestEntry(eldest)) {
                remove(eldest.key);
            }
    
            // Create new entry, link it on to list, and put it into table
            LinkedEntry<K, V> oldTail = header.prv;
            LinkedEntry<K, V> newTail = new LinkedEntry<K,V>(
                    key, value, hash, table[index], header, oldTail);
            table[index] = oldTail.nxt = header.prv = newTail;
        }
    

    每次 add 一个新的 Entry,都是插入到 Header 和 Tail 之间,即插入的 Entry 作为新的 Tail。

    removeEldestEntry() 一直返回 false,需要子类自己根据需要实现。

    看 get 方法:

        /**
         * Returns the value of the mapping with the specified key.
         *
         * @param key
         *            the key.
         * @return the value of the mapping with the specified key, or {@code null}
         *         if no mapping for the specified key is found.
         */
        @Override public V get(Object key) {
            /*
             * This method is overridden to eliminate the need for a polymorphic
             * invocation in superclass at the expense of code duplication.
             */
            if (key == null) {
                HashMapEntry<K, V> e = entryForNullKey;
                if (e == null)
                    return null;
                if (accessOrder)
                    makeTail((LinkedEntry<K, V>) e);
                return e.value;
            }
    
            int hash = Collections.secondaryHash(key);
            HashMapEntry<K, V>[] tab = table;
            for (HashMapEntry<K, V> e = tab[hash & (tab.length - 1)];
                    e != null; e = e.next) {
                K eKey = e.key;
                if (eKey == key || (e.hash == hash && key.equals(eKey))) {
                    if (accessOrder)
                        makeTail((LinkedEntry<K, V>) e);
                    return e.value;
                }
            }
            return null;
        }
    

    与 HashMap 的 get() 差不多,不过多了

    if (accessOrder)
        makeTail((LinkedEntry<K, V>) e);

    这两行代码,看看 makeTail():

        /**
         * Relinks the given entry to the tail of the list. Under access ordering,
         * this method is invoked whenever the value of a  pre-existing entry is
         * read by Map.get or modified by Map.put.
         */
        private void makeTail(LinkedEntry<K, V> e) {
            // Unlink e
            e.prv.nxt = e.nxt;
            e.nxt.prv = e.prv;
    
            // Relink e as tail
            LinkedEntry<K, V> header = this.header;
            LinkedEntry<K, V> oldTail = header.prv;
            e.nxt = header;
            e.prv = oldTail;
            oldTail.nxt = header.prv = e;
            modCount++;
        }
    

    作用就是将 e 给插入到 header 和 tail 之间,和 addNewEntry() 的逻辑一样。

    accessOrder 的作用,为 true 表示按照访问顺序排序,最近访问的放到链表的最后(双向链表也没有前后之分)。

    为 false 表示按照插入顺序排序,最后插入的方法链表的最后,从上面的方法里面很容易看出来。

    preModify 也调整了访问顺序:

        @Override void preModify(HashMapEntry<K, V> e) {
            if (accessOrder) {
                makeTail((LinkedEntry<K, V>) e);
            }
        }
    

    其他的方法暂时先放一边,就这样。

    根据 LinkedHashMap 实现的 LruMemeryCache:

    /**
     * A cache that holds strong references to a limited number of Bitmaps. Each time a Bitmap is accessed, it is moved to
     * the head of a queue. When a Bitmap is added to a full cache, the Bitmap at the end of that queue is evicted and may
     * become eligible for garbage collection.<br />
     * <br />
     * <b>NOTE:</b> This cache uses only strong references for stored Bitmaps.
     *
     * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
     * @since 1.8.1
     */
    public class LruMemoryCache implements MemoryCache {
    
    	private final LinkedHashMap<String, Bitmap> map;
    
    	private final int maxSize;
    	/** Size of this cache in bytes */
    	private int size;
    
    	/** @param maxSize Maximum sum of the sizes of the Bitmaps in this cache */
    	public LruMemoryCache(int maxSize) {
    		if (maxSize <= 0) {
    			throw new IllegalArgumentException("maxSize <= 0");
    		}
    		this.maxSize = maxSize;
    		this.map = new LinkedHashMap<String, Bitmap>(0, 0.75f, true);
    	}
    
    	/**
    	 * Returns the Bitmap for {@code key} if it exists in the cache. If a Bitmap was returned, it is moved to the head
    	 * of the queue. This returns null if a Bitmap is not cached.
    	 */
    	@Override
    	public final Bitmap get(String key) {
    		if (key == null) {
    			throw new NullPointerException("key == null");
    		}
    
    		synchronized (this) {
    			return map.get(key);
    		}
    	}
    
    	/** Caches {@code Bitmap} for {@code key}. The Bitmap is moved to the head of the queue. */
    	@Override
    	public final boolean put(String key, Bitmap value) {
    		if (key == null || value == null) {
    			throw new NullPointerException("key == null || value == null");
    		}
    
    		synchronized (this) {
    			size += sizeOf(key, value);
    			Bitmap previous = map.put(key, value);
    			if (previous != null) {
    				size -= sizeOf(key, previous);
    			}
    		}
    
    		trimToSize(maxSize);
    		return true;
    	}
    
    	/**
    	 * Remove the eldest entries until the total of remaining entries is at or below the requested size.
    	 *
    	 * @param maxSize the maximum size of the cache before returning. May be -1 to evict even 0-sized elements.
    	 */
    	private void trimToSize(int maxSize) {
    		while (true) {
    			String key;
    			Bitmap value;
    			synchronized (this) {
    				if (size < 0 || (map.isEmpty() && size != 0)) {
    					throw new IllegalStateException(getClass().getName() + ".sizeOf() is reporting inconsistent results!");
    				}
    
    				if (size <= maxSize || map.isEmpty()) {
    					break;
    				}
    
    				Map.Entry<String, Bitmap> toEvict = map.entrySet().iterator().next();
    				if (toEvict == null) {
    					break;
    				}
    				key = toEvict.getKey();
    				value = toEvict.getValue();
    				map.remove(key);
    				size -= sizeOf(key, value);
    			}
    		}
    	}
    
    	/** Removes the entry for {@code key} if it exists. */
    	@Override
    	public final Bitmap remove(String key) {
    		if (key == null) {
    			throw new NullPointerException("key == null");
    		}
    
    		synchronized (this) {
    			Bitmap previous = map.remove(key);
    			if (previous != null) {
    				size -= sizeOf(key, previous);
    			}
    			return previous;
    		}
    	}
    
    	@Override
    	public Collection<String> keys() {
    		synchronized (this) {
    			return new HashSet<String>(map.keySet());
    		}
    	}
    
    	@Override
    	public void clear() {
    		trimToSize(-1); // -1 will evict 0-sized elements
    	}
    
    	/**
    	 * Returns the size {@code Bitmap} in bytes.
    	 * <p/>
    	 * An entry's size must not change while it is in the cache.
    	 */
    	private int sizeOf(String key, Bitmap value) {
    		return value.getRowBytes() * value.getHeight();
    	}
    
    	@Override
    	public synchronized final String toString() {
    		return String.format("LruCache[maxSize=%d]", maxSize);
    	}
    }
    

      

  • 相关阅读:
    笔记35 跨重定向请求传递数
    判断邮箱的正则表达式
    按钮
    async await 的用法
    笔记34 Spring MVC的高级技术——处理multipart形式的数据
    Convert Sorted Array to Binary Search Tree
    Binary Tree Zigzag Level Order Traversal
    Unique Binary Search Trees,Unique Binary Search Trees II
    Validate Binary Search Tree
    Populating Next Right Pointers in Each Node,Populating Next Right Pointers in Each Node II
  • 原文地址:https://www.cnblogs.com/aprz512/p/5333918.html
Copyright © 2011-2022 走看看