• 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);
    	}
    }
    

      

  • 相关阅读:
    vue-cli快速搭建
    js严格模式下判断数据类型
    js实现本地的图片压缩上传预览
    web端实现图片放大切换显示预览
    swiper.js在隐藏/显示切换时,轮播出现bug的解决办法
    Zepto.js实现fadeIn,fadeOut功能
    ms
    redis 解决秒杀
    单下滑线 事务 锁
    极验
  • 原文地址:https://www.cnblogs.com/aprz512/p/5333918.html
Copyright © 2020-2023  润新知