All Downloads are FREE. Search and download functionalities are using the official Maven repository.

uk.org.retep.util.reference.DelayedWeakHashMap Maven / Gradle / Ivy

There is a newer version: 10.6
Show newest version
/*
 * 

Copyright (c) 1998-2009, Peter T Mount
* All rights reserved.

* *

Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met:

* *
    *
  • Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer.
  • * *
  • Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution.
  • * *
  • Neither the name of the retep.org.uk nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission.
  • * *
* *

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*/ package uk.org.retep.util.reference; import java.io.IOException; import java.lang.ref.WeakReference; import java.util.AbstractCollection; import java.util.AbstractMap; import java.util.AbstractSet; import java.util.Collection; import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import net.jcip.annotations.ThreadSafe; import uk.org.retep.annotations.Contract; import uk.org.retep.annotations.ReadLock; import uk.org.retep.annotations.WriteLock; /** * A {@link Map} implementation who's values are stored using * {@link java.lang.ref.WeakReference}'s. It's used where a key/value pair * is to be stored but when the value is no longer referenced after a set period * of time it is removed from memory. * * @param the type of keys maintained by this map * @param the type of mapped values * @author peter * @since 9.6 */ @ThreadSafe public class DelayedWeakHashMap extends AbstractMap implements Map { private static final long DEFAULT_DELAY = 15000L; private final ReadWriteLock lock = new ReentrantReadWriteLock(); private final long delay; static final int DEFAULT_INITIAL_CAPACITY = 16; static final int MAXIMUM_CAPACITY = 1 << 30; static final float DEFAULT_LOAD_FACTOR = 0.75f; transient Entry[] table; transient int size; int threshold; final float loadFactor; transient volatile int modCount; protected KeySet keySet; protected Values values; public DelayedWeakHashMap() { this( DEFAULT_DELAY ); } public DelayedWeakHashMap( final Map map ) { this( DEFAULT_DELAY, map ); } public DelayedWeakHashMap( final int initialCapacity ) { this( DEFAULT_DELAY, initialCapacity ); } public DelayedWeakHashMap( final int initialCapacity, final float loadFactor ) { this( DEFAULT_DELAY, initialCapacity, loadFactor ); } public DelayedWeakHashMap( final long delay, final int initialCapacity ) { this( DEFAULT_DELAY, initialCapacity, DEFAULT_LOAD_FACTOR ); } public DelayedWeakHashMap( final long delay ) { this.delay = delay; this.loadFactor = DEFAULT_LOAD_FACTOR; threshold = (int) (DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR); table = new Entry[ DEFAULT_INITIAL_CAPACITY ]; init(); } public DelayedWeakHashMap( final long delay, int initialCapacity, float loadFactor ) { this.delay = delay; 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 ); } // Find a power of 2 >= initialCapacity int capacity = 1; while( capacity < initialCapacity ) { capacity <<= 1; } this.loadFactor = loadFactor; threshold = (int) (capacity * loadFactor); table = new Entry[ capacity ]; init(); } public DelayedWeakHashMap( final long delay, Map m ) { this( delay, Math.max( (int) (m.size() / DEFAULT_LOAD_FACTOR) + 1, DEFAULT_INITIAL_CAPACITY ), DEFAULT_LOAD_FACTOR ); putAllForCreate( m ); } /** * Used for concurrency * @return */ @Contract( ReadLock.class ) protected final java.util.concurrent.locks.Lock readLock() { return lock.readLock(); } /** * Used for concurrency * @return */ @Contract( WriteLock.class ) protected final java.util.concurrent.locks.Lock writeLock() { return lock.writeLock(); } /** * Initialization hook for subclasses. This method is called * in all constructors and pseudo-constructors (clone, readObject) * after HashMap has been initialized but before any entries have * been inserted. (In the absence of this method, readObject would * require explicit knowledge of subclasses.) */ protected void init() { } /** * Applies a supplemental hash function to a given hashCode, which * defends against poor quality hash functions. This is critical * because HashMap uses power-of-two length hash tables, that * otherwise encounter collisions for hashCodes that do not differ * in lower bits. Note: Null keys always map to hash 0, thus index 0. */ static int hash( int h ) { // This function ensures that hashCodes that differ only by // constant multiples at each bit position have a bounded // number of collisions (approximately 8 at default load factor). h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); } /** * Returns index for hash code h. */ static int indexFor( int h, int length ) { return h & (length - 1); } /** * Returns the number of key-value mappings in this map. * * @return the number of key-value mappings in this map */ @ReadLock @Override public int size() { return size; } /** * Returns true if this map contains no key-value mappings. * * @return true if this map contains no key-value mappings */ @ReadLock @Override public boolean isEmpty() { return size == 0; } /** * Returns the value to which the specified key is mapped, * or {@code null} if this map contains no mapping for the key. * *

More formally, if this map contains a mapping from a key * {@code k} to a value {@code v} such that {@code (key==null ? k==null : * key.equals(k))}, then this method returns {@code v}; otherwise * it returns {@code null}. (There can be at most one such mapping.) * *

A return value of {@code null} does not necessarily * indicate that the map contains no mapping for the key; it's also * possible that the map explicitly maps the key to {@code null}. * The {@link #containsKey containsKey} operation may be used to * distinguish these two cases. * * @see #put(Object, Object) */ @ReadLock @Override public V get( Object key ) { if( key == null ) { return getForNullKey(); } int hash = hash( key.hashCode() ); for( Entry e = table[indexFor( hash, table.length )]; e != null; e = e.next ) { Object k; if( e.hash == hash && ((k = e.key) == key || key.equals( k )) ) { return e.getValue(); } } return null; } /** * Offloaded version of get() to look up null keys. Null keys map * to index 0. This null case is split out into separate methods * for the sake of performance in the two most commonly used * operations (get and put), but incorporated with conditionals in * others. */ private V getForNullKey() { for( Entry e = table[0]; e != null; e = e.next ) { if( e.key == null ) { return e.getValue(); } } return null; } /** * Returns true if this map contains a mapping for the * specified key. * * @param key The key whose presence in this map is to be tested * @return true if this map contains a mapping for the specified * key. */ @ReadLock @Override public boolean containsKey( Object key ) { return getEntry( key ) != null; } /** * Returns the entry associated with the specified key in the * HashMap. Returns null if the HashMap contains no mapping * for the key. */ @ReadLock final Entry getEntry( Object key ) { int hash = (key == null) ? 0 : hash( key.hashCode() ); for( Entry e = table[indexFor( hash, table.length )]; e != null; e = e.next ) { Object k; if( e.hash == hash && ((k = e.key) == key || (key != null && key.equals( k ))) ) { return e; } } return null; } /** * Associates the specified value with the specified key in this map. * If the map previously contained a mapping for the key, the old * value is replaced. * * @param key key with which the specified value is to be associated * @param value value to be associated with the specified key * @return the previous value associated with key, or * null if there was no mapping for key. * (A null return can also indicate that the map * previously associated null with key.) */ @WriteLock @Override public V put( K key, V value ) { if( key == null ) { return putForNullKey( value ); } int hash = hash( key.hashCode() ); int i = indexFor( hash, table.length ); for( Entry e = table[i]; e != null; e = e.next ) { Object k; if( e.hash == hash && ((k = e.key) == key || key.equals( k )) ) { V oldValue = e.setValue( value ); e.recordAccess( this ); return oldValue; } } modCount++; addEntry( hash, key, value, i ); return null; } /** * Offloaded version of put for null keys */ private V putForNullKey( V value ) { for( Entry e = table[0]; e != null; e = e.next ) { if( e.key == null ) { V oldValue = e.setValue( value ); e.recordAccess( this ); return oldValue; } } modCount++; addEntry( 0, null, value, 0 ); return null; } /** * This method is used instead of put by constructors and * pseudoconstructors (clone, readObject). It does not resize the table, * check for comodification, etc. It calls createEntry rather than * addEntry. */ private void putForCreate( K key, V value ) { int hash = (key == null) ? 0 : hash( key.hashCode() ); int i = indexFor( hash, table.length ); /** * Look for preexisting entry for key. This will never happen for * clone or deserialize. It will only happen for construction if the * input Map is a sorted map whose ordering is inconsistent w/ equals. */ for( Entry e = table[i]; e != null; e = e.next ) { Object k; if( e.hash == hash && ((k = e.key) == key || (key != null && key.equals( k ))) ) { e.setValue( value ); return; } } createEntry( hash, key, value, i ); } private void putAllForCreate( Map m ) { for( Iterator> i = m.entrySet().iterator(); i.hasNext(); ) { Map.Entry e = i.next(); putForCreate( e.getKey(), e.getValue() ); } } /** * Rehashes the contents of this map into a new array with a * larger capacity. This method is called automatically when the * number of keys in this map reaches its threshold. * * If current capacity is MAXIMUM_CAPACITY, this method does not * resize the map, but sets threshold to Integer.MAX_VALUE. * This has the effect of preventing future calls. * * @param newCapacity the new capacity, MUST be a power of two; * must be greater than current capacity unless current * capacity is MAXIMUM_CAPACITY (in which case value * is irrelevant). */ void resize( int newCapacity ) { Entry[] oldTable = table; int oldCapacity = oldTable.length; if( oldCapacity == MAXIMUM_CAPACITY ) { threshold = Integer.MAX_VALUE; return; } Entry[] newTable = new Entry[ newCapacity ]; transfer( newTable ); table = newTable; threshold = (int) (newCapacity * loadFactor); } /** * Transfers all entries from current table to newTable. */ void transfer( Entry[] newTable ) { Entry[] src = table; int newCapacity = newTable.length; for( int j = 0; j < src.length; j++ ) { Entry e = src[j]; if( e != null ) { src[j] = null; do { Entry next = e.next; int i = indexFor( e.hash, newCapacity ); e.next = newTable[i]; newTable[i] = e; e = next; } while( e != null ); } } } /** * Copies all of the mappings from the specified map to this map. * These mappings will replace any mappings that this map had for * any of the keys currently in the specified map. * * @param m mappings to be stored in this map * @throws NullPointerException if the specified map is null */ @WriteLock @Override public void putAll( Map m ) { int numKeysToBeAdded = m.size(); if( numKeysToBeAdded == 0 ) { return; } /* * Expand the map if the map if the number of mappings to be added * is greater than or equal to threshold. This is conservative; the * obvious condition is (m.size() + size) >= threshold, but this * condition could result in a map with twice the appropriate capacity, * if the keys to be added overlap with the keys already in this map. * By using the conservative calculation, we subject ourself * to at most one extra resize. */ 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( Iterator> i = m.entrySet().iterator(); i.hasNext(); ) { Map.Entry e = i.next(); put( e.getKey(), e.getValue() ); } } /** * Removes the mapping for the specified key from this map if present. * * @param key key whose mapping is to be removed from the map * @return the previous value associated with key, or * null if there was no mapping for key. * (A null return can also indicate that the map * previously associated null with key.) */ @WriteLock @Override public V remove( Object key ) { Entry e = removeEntryForKey( key ); return (e == null ? null : e.getValue()); } /** * Removes and returns the entry associated with the specified key * in the HashMap. Returns null if the HashMap contains no mapping * for this key. */ @WriteLock final Entry removeEntryForKey( Object key ) { int hash = (key == null) ? 0 : hash( key.hashCode() ); int i = indexFor( hash, table.length ); Entry prev = table[i]; Entry e = prev; while( e != null ) { Entry next = e.next; Object k; if( e.hash == hash && ((k = e.key) == key || (key != null && key.equals( k ))) ) { modCount++; size--; if( prev == e ) { table[i] = next; } else { prev.next = next; } e.recordRemoval( this ); return e; } prev = e; e = next; } return e; } /** * Special version of remove for EntrySet. */ final Entry removeMapping( Object o ) { if( !(o instanceof Map.Entry) ) { return null; } Map.Entry entry = (Map.Entry) o; Object key = entry.getKey(); int hash = (key == null) ? 0 : hash( key.hashCode() ); int i = indexFor( hash, table.length ); Entry prev = table[i]; Entry e = prev; while( e != null ) { Entry next = e.next; if( e.hash == hash && e.equals( entry ) ) { modCount++; size--; if( prev == e ) { table[i] = next; } else { prev.next = next; } e.recordRemoval( this ); return e; } prev = e; e = next; } return e; } /** * Removes all of the mappings from this map. * The map will be empty after this call returns. */ @WriteLock @Override public void clear() { modCount++; Entry[] tab = table; for( int i = 0; i < tab.length; i++ ) { tab[i] = null; } size = 0; } /** * Returns true if this map maps one or more keys to the * specified value. * * @param value value whose presence in this map is to be tested * @return true if this map maps one or more keys to the * specified value */ @ReadLock public boolean containsValue( Object value ) { if( value == null ) { return containsNullValue(); } Entry[] tab = table; for( int i = 0; i < tab.length; i++ ) { for( Entry e = tab[i]; e != null; e = e.next ) { if( value.equals( e.value ) ) { return true; } } } return false; } /** * Special-case code for containsValue with null argument */ private boolean containsNullValue() { Entry[] tab = table; for( int i = 0; i < tab.length; i++ ) { for( Entry e = tab[i]; e != null; e = e.next ) { if( e.value == null ) { return true; } } } return false; } /** * Returns a shallow copy of this HashMap instance: the keys and * values themselves are not cloned. * * @return a shallow copy of this map */ @ReadLock public Object clone() { DelayedWeakHashMap result = null; try { result = (DelayedWeakHashMap) super.clone(); } catch( CloneNotSupportedException e ) { // assert false; } result.table = new Entry[ table.length ]; result.entrySet = null; result.modCount = 0; result.size = 0; result.init(); result.putAllForCreate( this ); return result; } /** * The delay that an Entry will remain in the map after it's last access * until it is removed from the Map * @return delay in milliseconds */ public final long getDelay() { return delay; } class Ref extends AbstractDelayedWeakReference { private final K key; public Ref( final K key, final V v ) { super( getDelay(), v ); this.key = key; } @Override protected void remove( final V o ) { DelayedWeakHashMap.this.removeEntryForKey( key ); } @Override public String toString() { return key == null ? null : key.toString(); } } @ThreadSafe class Entry implements Map.Entry { final K key; Ref value; Entry next; final int hash; /** * Creates new entry. */ Entry( int h, K k, V v, Entry n ) { value = new Ref( k, v ); next = n; key = k; hash = h; } @Contract( ReadLock.class ) protected final java.util.concurrent.locks.Lock readLock() { return DelayedWeakHashMap.this.readLock(); } @Contract( WriteLock.class ) protected final java.util.concurrent.locks.Lock writeLock() { return DelayedWeakHashMap.this.writeLock(); } @ReadLock @Override public final K getKey() { return key; } @ReadLock @Override public final V getValue() { return value.get(); } @WriteLock @Override public final V setValue( V newValue ) { final V oldValue = value.getAndRelease(); value = new Ref( key, newValue ); return oldValue; } @Override public final boolean equals( Object o ) { if( !(o instanceof Map.Entry) ) { return false; } Map.Entry e = (Map.Entry) o; Object k1 = getKey(); Object k2 = e.getKey(); if( k1 == k2 || (k1 != null && k1.equals( k2 )) ) { Object v1 = getValue(); Object v2 = e.getValue(); if( v1 == v2 || (v1 != null && v1.equals( v2 )) ) { return true; } } return false; } @Override public final int hashCode() { return (key == null ? 0 : key.hashCode()) ^ (value == null ? 0 : value.getHashCode()); } @Override public final String toString() { return getKey() + "=" + getValue(); } /** * This method is invoked whenever the value in an entry is * overwritten by an invocation of put(k,v) for a key k that's already * in the HashMap. */ void recordAccess( DelayedWeakHashMap m ) { } /** * This method is invoked whenever the entry is * removed from the table. */ void recordRemoval( DelayedWeakHashMap m ) { // Tell the gc that we are no longer managing the value value.getAndRelease(); value = null; } } /** * Adds a new entry with the specified key, value and hash code to * the specified bucket. It is the responsibility of this * method to resize the table if appropriate. * * Subclass overrides this to alter the behavior of put method. */ void addEntry( int hash, K key, V value, int bucketIndex ) { Entry e = table[bucketIndex]; table[bucketIndex] = new Entry( hash, key, value, e ); if( size++ >= threshold ) { resize( 2 * table.length ); } } /** * Like addEntry except that this version is used when creating entries * as part of Map construction or "pseudo-construction" (cloning, * deserialization). This version needn't worry about resizing the table. * * Subclass overrides this to alter the behavior of HashMap(Map), * clone, and readObject. */ void createEntry( int hash, K key, V value, int bucketIndex ) { Entry e = table[bucketIndex]; table[bucketIndex] = new Entry( hash, key, value, e ); size++; } private abstract class HashIterator implements Iterator { Entry next; // next entry to return int expectedModCount; // For fast-fail int index; // current slot Entry current; // current entry HashIterator() { expectedModCount = modCount; if( size > 0 ) { // advance to first entry Entry[] t = table; while( index < t.length && (next = t[index++]) == null ); } } @Override public final boolean hasNext() { return next != null; } @SuppressWarnings( "empty-statement" ) final Entry nextEntry() { if( modCount != expectedModCount ) { throw new ConcurrentModificationException(); } Entry e = next; if( e == null ) { throw new NoSuchElementException(); } if( (next = e.next) == null ) { Entry[] t = table; while( index < t.length && (next = t[index++]) == null ); } current = e; return e; } @Override public void remove() { if( current == null ) { throw new IllegalStateException(); } if( modCount != expectedModCount ) { throw new ConcurrentModificationException(); } Object k = current.key; current = null; DelayedWeakHashMap.this.removeEntryForKey( k ); expectedModCount = modCount; } } private final class ValueIterator extends HashIterator { @Override public V next() { return nextEntry().getValue(); } } private final class KeyIterator extends HashIterator { public K next() { return nextEntry().getKey(); } } private final class EntryIterator extends HashIterator> { @Override public Map.Entry next() { return nextEntry(); } } // Subclass overrides these to alter behavior of views' iterator() method Iterator newKeyIterator() { return new KeyIterator(); } Iterator newValueIterator() { return new ValueIterator(); } Iterator> newEntryIterator() { return new EntryIterator(); } // Views private transient Set> entrySet = null; /** * Returns a {@link Set} view of the keys contained in this map. * The set is backed by the map, so changes to the map are * reflected in the set, and vice-versa. If the map is modified * while an iteration over the set is in progress (except through * the iterator's own remove operation), the results of * the iteration are undefined. The set supports element removal, * which removes the corresponding mapping from the map, via the * Iterator.remove, Set.remove, * removeAll, retainAll, and clear * operations. It does not support the add or addAll * operations. */ @Override @WriteLock public Set keySet() { Set ks = keySet; return (ks != null ? ks : (keySet = new KeySet())); } @ThreadSafe private final class KeySet extends AbstractSet { @Contract( ReadLock.class ) protected final java.util.concurrent.locks.Lock readLock() { return DelayedWeakHashMap.this.readLock(); } @Contract( WriteLock.class ) protected final java.util.concurrent.locks.Lock writeLock() { return DelayedWeakHashMap.this.writeLock(); } @Override @ReadLock public Iterator iterator() { return newKeyIterator(); } @Override @ReadLock public int size() { return size; } @Override public boolean contains( Object o ) { return containsKey( o ); } @Override @WriteLock public boolean remove( Object o ) { return DelayedWeakHashMap.this.removeEntryForKey( o ) != null; } @Override public void clear() { DelayedWeakHashMap.this.clear(); } } /** * Returns a {@link Collection} view of the values contained in this map. * The collection is backed by the map, so changes to the map are * reflected in the collection, and vice-versa. If the map is * modified while an iteration over the collection is in progress * (except through the iterator's own remove operation), * the results of the iteration are undefined. The collection * supports element removal, which removes the corresponding * mapping from the map, via the Iterator.remove, * Collection.remove, removeAll, * retainAll and clear operations. It does not * support the add or addAll operations. */ @Override @WriteLock public Collection values() { Collection vs = values; return (vs != null ? vs : (values = new Values())); } @ThreadSafe private final class Values extends AbstractCollection { @Contract( ReadLock.class ) protected final java.util.concurrent.locks.Lock readLock() { return DelayedWeakHashMap.this.readLock(); } @Contract( WriteLock.class ) protected final java.util.concurrent.locks.Lock writeLock() { return DelayedWeakHashMap.this.writeLock(); } @ReadLock public Iterator iterator() { return newValueIterator(); } @Override @ReadLock public int size() { return size; } @Override public boolean contains( Object o ) { return containsValue( o ); } @Override public void clear() { DelayedWeakHashMap.this.clear(); } } /** * Returns a {@link Set} view of the mappings contained in this map. * The set is backed by the map, so changes to the map are * reflected in the set, and vice-versa. If the map is modified * while an iteration over the set is in progress (except through * the iterator's own remove operation, or through the * setValue operation on a map entry returned by the * iterator) the results of the iteration are undefined. The set * supports element removal, which removes the corresponding * mapping from the map, via the Iterator.remove, * Set.remove, removeAll, retainAll and * clear operations. It does not support the * add or addAll operations. * * @return a set view of the mappings contained in this map */ @Override public Set> entrySet() { return entrySet0(); } @WriteLock private Set> entrySet0() { Set> es = entrySet; return es != null ? es : (entrySet = new EntrySet()); } @ThreadSafe private final class EntrySet extends AbstractSet> { @Contract( ReadLock.class ) protected final java.util.concurrent.locks.Lock readLock() { return DelayedWeakHashMap.this.readLock(); } @Contract( WriteLock.class ) protected final java.util.concurrent.locks.Lock writeLock() { return DelayedWeakHashMap.this.writeLock(); } @Override public Iterator> iterator() { return newEntryIterator(); } @Override @ReadLock public boolean contains( Object o ) { if( !(o instanceof Map.Entry) ) { return false; } Map.Entry e = (Map.Entry) o; Entry candidate = getEntry( e.getKey() ); return candidate != null && candidate.equals( e ); } @Override @WriteLock public boolean remove( Object o ) { return removeMapping( o ) != null; } @Override @ReadLock public int size() { return size; } @Override public void clear() { DelayedWeakHashMap.this.clear(); } } /** * Save the state of the HashMap instance to a stream (i.e., * serialize it). * * We don't actually store the contents as it's weak. */ private void writeObject( java.io.ObjectOutputStream s ) throws IOException { Iterator> i = (size > 0) ? entrySet0().iterator() : null; // Write out the threshold, loadfactor, and any hidden stuff s.defaultWriteObject(); } private static final long serialVersionUID = 362498820763181265L; /** * Reconstitute the HashMap instance from a stream (i.e., * deserialize it). */ private void readObject( java.io.ObjectInputStream s ) throws IOException, ClassNotFoundException { // Read in the threshold, loadfactor, and any hidden stuff s.defaultReadObject(); table = new Entry[ DEFAULT_INITIAL_CAPACITY ]; init(); // Give subclass a chance to do its thing. size = 0; } float loadFactor() { return loadFactor; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy