//A Map object based on weak references // first look at the constructor public WeakHashMap() { this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR); } public WeakHashMap(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR); } public WeakHashMap(Map<? extends K, ? extends V> m) { this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1, DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR); putAll(m); } public WeakHashMap(int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Initial Capacity: "+ initialCapacity); if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal Load factor: "+ loadFactor); int capacity = 1; while (capacity < initialCapacity) capacity <<= 1; table = newTable(capacity); this.loadFactor = loadFactor; threshold = (int)(capacity * loadFactor); useAltHashing = sun.misc.VM.isBooted() && (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD); } // add element public V put(K key, V value) { // Convert the key value of null to an object Object k = maskNull(key); //Calculate the hash value int h = hash(k); //Get the entire entry, which will delete the expired key Entry<K,V>[] tab = getTable(); int i = indexFor(h, tab.length); for (Entry<K,V> e = tab[i]; e != null; e = e.next) { //if the key already exists if (h == e.hash && eq(k, e.get())) { V oldValue = e.value; if (value != oldValue) e.value = value; return oldValue; } } modCount++; Entry<K,V> e = tab[i]; // insert at the first position tab[i] = new Entry<>(k, value, queue, h, e); //expand if (++size >= threshold) resize(tab.length * 2); return null; } private static Object maskNull(Object key) { return (key == null) ? NULL_KEY : key; } private static final Object NULL_KEY = new Object(); private Entry<K,V>[] getTable() { expungeStaleEntries(); return table; } // delete the expired key private void expungeStaleEntries() { for (Object x; (x = queue.poll()) != null; ) { synchronized (queue) { @SuppressWarnings("unchecked") Entry<K,V> e = (Entry<K,V>) x; int i = indexFor(e.hash, table.length); Entry<K,V> prev = table[i]; Entry<K,V> p = prev; while (p != null) { Entry<K,V> next = p.next; if (p == e) { // delete the head of the queue at this time if (prev == e) table[i] = next; else prev.next = next; // Must not null out e.next; // stale entries may be in use by a HashIterator // prevent memory leaks e.value = null; // Help GC size--; break; } prev = p; p = next; } } } } void resize(int newCapacity) { Entry<K,V>[] oldTable = getTable(); int oldCapacity = oldTable.length; if (oldCapacity == MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return; } Entry<K,V>[] newTable = newTable(newCapacity); boolean oldAltHashing = useAltHashing; useAltHashing |= sun.misc.VM.isBooted() && (newCapacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD); boolean rehash = oldAltHashing ^ useAltHashing; transfer(oldTable, newTable, rehash); table = newTable; if (size >= threshold / 2) { threshold = (int)(newCapacity * loadFactor); } else { expungeStaleEntries(); transfer(newTable, oldTable, false); table = oldTable; } } // Construct a new entry private void transfer(Entry<K,V>[] src, Entry<K,V>[] dest, boolean rehash) { for (int j = 0; j < src.length; ++j) { Entry<K,V> e = src[j]; src[j] = null; while (e != null) { Entry<K,V> next = e.next; Object key = e.get(); if (key == null) { e.next = null; // Help GC e.value = null; // " " size--; } else { if (rehash) { e.hash = hash(key); } int i = indexFor (e.hash, dest.length); e.next = dest[i]; dest[i] = e; } e = next; } } } /** The process of inserting elements is as above. Now the question is how to recycle automatically? */ //Look at this code when inserting tab[i] = new Entry<>(k, value, queue, h, e); //then see this entry private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> { V value; int hash; Entry<K,V> next; /** * Creates new entry. */ Entry(Object key, V value, ReferenceQueue<Object> queue, int hash, Entry<K,V> next) { super(key, queue); this.value = value; this.hash = hash; this.next = next; } //then just public abstract class Reference<T> //There is a thread inside private static class ReferenceHandler extends Thread { ReferenceHandler(ThreadGroup g, String name) { super(g, name); } public void run() { for (;;) { Reference r; synchronized (lock) { if (pending != null) { //assigned by the virtual machine r = pending; Reference rn = r.next; pending = (rn == r) ? null : rn; r.next = r; } else { try { lock.wait(); } catch (InterruptedException x) { } continue; } } // Fast path for cleaners if (r instanceof Cleaner) { ((Cleaner)r).clean(); continue; } ReferenceQueue q = r.queue; // enqueue if (q != ReferenceQueue.NULL) q.enqueue(r); } } } boolean enqueue(Reference<? extends T> r) { /* Called only by Reference class */ synchronized (r) { if (r.queue == ENQUEUED) return false; synchronized (lock) { r.queue = ENQUEUED; r.next = (head == null) ? r : head; head = r; queueLength++; if (r instanceof FinalReference) { sun.misc.VM.addFinalRefCount(1); } lock.notifyAll(); return true; } } } /** Summary: Every time an element is put, it first finds the expired key from the ReferenceQueue and then deletes it. Then every time the entry is created, the background will manage a group of threads through ThreadGroup. This group of threads is mainly The jvm assigns an expired key to the Reference. Then add it to the queue of ReferenceQueue. */ public void putAll(Map<? extends K, ? extends V> m) { int numKeysToBeAdded = m.size(); if (numKeysToBeAdded == 0) return; //First calculate whether expansion is needed to improve efficiency if (numKeysToBeAdded > threshold) { int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1); if (targetCapacity > MAXIMUM_CAPACITY) targetCapacity = MAXIMUM_CAPACITY; int newCapacity = table.length; while (newCapacity < targetCapacity) newCapacity <<= 1; if (newCapacity > table.length) resize(newCapacity); } for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) put(e.getKey(), e.getValue()); } // get value by key public V get(Object key) { Object k = maskNull(key); int h = hash(k); Entry<K,V>[] tab = getTable(); int index = indexFor(h, tab.length); Entry<K,V> e = tab[index]; while (e != null) { if (e.hash == h && eq(k, e.get())) return e.value; e = e.next; } return null; } //return length public int size() { if (size == 0) return 0; expungeStaleEntries(); return size; } //Whether the entire map is empty public boolean isEmpty() { return size() == 0; } // does it contain a key public boolean containsKey(Object key) { return getEntry(key) != null; } Entry<K,V> getEntry(Object key) { Object k = maskNull(key); int h = hash(k); Entry<K,V>[] tab = getTable(); int index = indexFor(h, tab.length); Entry<K,V> e = tab[index]; while (e != null && !(e.hash == h && eq(k, e.get()))) e = e.next; return e; } // delete by key public V remove(Object key) { Object k = maskNull(key); int h = hash(k); Entry<K,V>[] tab = getTable(); int i = indexFor(h, tab.length); Entry<K,V> prev = tab[i]; Entry<K,V> e = prev; while (e != null) { Entry<K,V> next = e.next; if (h == e.hash && eq(k, e.get())) { modCount++; size--; if (prev == e) tab[i] = next; else prev.next = next; return e.value; } prev = e; e = next; } return null; } //delete entry boolean removeMapping(Object o) { if (!(o instanceof Map.Entry)) return false; Entry<K,V>[] tab = getTable(); Map.Entry<?,?> entry = (Map.Entry<?,?>)o; Object k = maskNull(entry.getKey()); int h = hash(k); int i = indexFor(h, tab.length); Entry<K,V> prev = tab[i]; Entry<K,V> e = prev; while (e != null) { Entry<K,V> next = e.next; if (h == e.hash && e.equals(entry)) { modCount++; size--; if (prev == e) tab[i] = next; else prev.next = next; return true; } prev = e; e = next; } return false; } // clear public void clear() { while (queue.poll() != null) ; modCount++; Arrays.fill(table, null); size = 0; while (queue.poll() != null) ; } // does it contain a value public boolean containsValue(Object value) { if (value==null) return containsNullValue(); Entry<K,V>[] tab = getTable(); for (int i = tab.length; i-- > 0;) for (Entry<K,V> e = tab[i]; e != null; e = e.next) if (value.equals(e.value)) return true; return false; }
Read WeakHashMap source code
Guess you like
Origin http://43.154.161.224:23101/article/api/json?id=326392586&siteId=291194637
Recommended
Ranking