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

org.infinispan.commons.util.WeakValueHashMap Maven / Gradle / Ivy

There is a newer version: 15.1.0.Dev04
Show newest version
package org.infinispan.commons.util;

import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.AbstractSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * This Map will remove entries when the value in the map has been cleaned from
 * garbage collection
 *
 * @param  the key type
 * @param  the value type
 * @author Bill Burke
 * @author Adrian Brock
 * @author Ales Justin
 * @see 
 *    JBoss Common Core source code for origins of this class
 */
public final class WeakValueHashMap extends java.util.AbstractMap {

   /**
    * Hash table mapping keys to ref values
    */
   private final Map> map;

   /**
    * Reference queue for cleared RefKeys
    */
   private final ReferenceQueue queue = new ReferenceQueue();

   /**
    * Constructs a new, empty WeakValueHashMap with the given
    * initial capacity and the given load factor.
    *
    * @param initialCapacity The initial capacity of the WeakValueHashMap
    * @param loadFactor      The load factor of the WeakValueHashMap
    * @throws IllegalArgumentException If the initial capacity is less than
    *                                  zero, or if the load factor is
    *                                  nonpositive
    */
   public WeakValueHashMap(int initialCapacity, float loadFactor) {
      map = createMap(initialCapacity, loadFactor);
   }

   /**
    * Constructs a new, empty WeakValueHashMap with the given
    * initial capacity and the default load factor, which is
    * 0.75.
    *
    * @param initialCapacity The initial capacity of the WeakValueHashMap
    * @throws IllegalArgumentException If the initial capacity is less than
    *                                  zero
    */
   public WeakValueHashMap(int initialCapacity) {
      map = createMap(initialCapacity);
   }

   /**
    * Constructs a new, empty WeakValueHashMap with the default
    * initial capacity and the default load factor, which is
    * 0.75.
    */
   public WeakValueHashMap() {
      map = createMap();
   }

   /**
    * Constructs a new WeakValueHashMap with the same mappings as
    * the specified Map.  The WeakValueHashMap is created
    * with an initial capacity of twice the number of mappings in the specified
    * map or 11 (whichever is greater), and a default load factor, which is
    * 0.75.
    *
    * @param t the map whose mappings are to be placed in this map.
    * @since 1.3
    */
   public WeakValueHashMap(Map t) {
      this(Math.max(2 * t.size(), 11), 0.75f);
      putAll(t);
   }

   /**
    * Create new value ref instance.
    *
    * @param key   the key
    * @param value the value
    * @param q     the ref queue
    * @return new value ref instance
    */
   private ValueRef create(K key, V value, ReferenceQueue q) {
      return WeakValueRef.create(key, value, q);
   }

   /**
    * Create map.
    *
    * @param initialCapacity the initial capacity
    * @param loadFactor      the load factor
    * @return new map instance
    */
   private Map> createMap(int initialCapacity, float loadFactor) {
      return new HashMap>(initialCapacity, loadFactor);
   }

   /**
    * Create map.
    *
    * @param initialCapacity the initial capacity
    * @return new map instance
    */
   private Map> createMap(int initialCapacity) {
      return new HashMap>(initialCapacity);
   }

   /**
    * Create map.
    *
    * @return new map instance
    */
   protected Map> createMap() {
      return new HashMap>();
   }

   @Override
   public int size() {
      processQueue();
      return map.size();
   }

   @Override
   public boolean containsKey(Object key) {
      processQueue();
      return map.containsKey(key);
   }

   @Override
   public V get(Object key) {
      processQueue();
      ValueRef ref = map.get(key);
      if (ref != null)
         return ref.get();
      return null;
   }

   @Override
   public V put(K key, V value) {
      processQueue();
      ValueRef ref = create(key, value, queue);
      ValueRef result = map.put(key, ref);
      if (result != null)
         return result.get();
      return null;
   }

   @Override
   public V remove(Object key) {
      processQueue();
      ValueRef result = map.remove(key);
      if (result != null)
         return result.get();
      return null;
   }

   @Override
   public Set> entrySet() {
      processQueue();
      return new EntrySet();
   }

   @Override
   public void clear() {
      processQueue();
      map.clear();
   }

   @Override
   public String toString() {
      return map.toString();
   }

   /**
    * Remove all entries whose values have been discarded.
    */
   @SuppressWarnings("unchecked")
   private void processQueue() {
      ValueRef ref = (ValueRef) queue.poll();
      while (ref != null) {
         // only remove if it is the *exact* same WeakValueRef
         if (ref == map.get(ref.getKey()))
            map.remove(ref.getKey());

         ref = (ValueRef) queue.poll();
      }
   }

   /**
    * EntrySet.
    */
   private class EntrySet extends AbstractSet> {

      @Override
      public Iterator> iterator() {
         return new EntrySetIterator(map.entrySet().iterator());
      }

      @Override
      public int size() {
         return WeakValueHashMap.this.size();
      }
   }

   /**
    * EntrySet iterator
    */
   private class EntrySetIterator implements Iterator> {

      /**
       * The delegate
       */
      private final Iterator>> delegate;

      /**
       * Create a new EntrySetIterator.
       *
       * @param delegate the delegate
       */
      public EntrySetIterator(Iterator>> delegate) {
         this.delegate = delegate;
      }

      public boolean hasNext() {
         return delegate.hasNext();
      }

      public Entry next() {
         Entry> next = delegate.next();
         return next.getValue();
      }

      public void remove() {
         throw new UnsupportedOperationException("remove");
      }
   }

   /**
    * Weak value ref.
    *
    * @param  the key type
    * @param  the value type
    * @author Bill Burke
    * @author Adrian Brock
    * @author Ales Justin
    */
   private static class WeakValueRef
         extends WeakReference implements ValueRef {

      /**
       * The key
       */
      public K key;

      /**
       * Safely create a new WeakValueRef
       *
       * @param  the key type
       * @param  the value type
       * @param key the key
       * @param val the value
       * @param q   the reference queue
       * @return the reference or null if the value is null
       */
      static  WeakValueRef create(K key, V val, ReferenceQueue q) {
         if (val == null)
            return null;
         else
            return new WeakValueRef(key, val, q);
      }

      /**
       * Create a new WeakValueRef.
       *
       * @param key the key
       * @param val the value
       * @param q   the reference queue
       */
      private WeakValueRef(K key, V val, ReferenceQueue q) {
         super(val, q);
         this.key = key;
      }

      public K getKey() {
         return key;
      }

      public V getValue() {
         return get();
      }

      public V setValue(V value) {
         throw new UnsupportedOperationException("setValue");
      }

      @Override
      public String toString() {
         return String.valueOf(get());
      }
   }

   public interface ValueRef extends Map.Entry {

      /**
       * Get underlying value.
       *
       * @return the value
       */
      V get();
   }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy