commons-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "christopher marshall" <oxbow_la...@hotmail.com>
Subject Re: PATCH : org.apache.commons.collections.SoftRefHashMap
Date Tue, 21 May 2002 09:51:01 GMT

It doesn't seem as if anyone has submitted this patch into the CVS 
repository for testing... Is this any way to encourage new participants?

Chris

>From: "christopher marshall" <oxbow_lakes@hotmail.com>
>Reply-To: "Jakarta Commons Developers List" 
><commons-dev@jakarta.apache.org>
>To: commons-dev@jakarta.apache.org
>Subject: PATCH : org.apache.commons.collections.SoftRefHashMap
>Date: Fri, 17 May 2002 16:03:33 +0000
>
>All -
>
>Please let me know if this is OK as I haven't submitted code before.
>
>Patches fix the SoftRefHashMap so that the "entrySet()" and "values()"
>methods are backed by the underlying map and the keys whose referent-values
>have been cleared will be automatically (and performant-ly) purged upon
>writes to the map. I have deprecated the "purge()" method and 
>re-implemented
>it to call "processQueue()", which is a more performant way of purging the
>Map as it goes straight to the cleared values without iterating through the
>whole Map.
>
>The Map also has the new method "getIfAbsentPut(Object key, ObjectFactory
>fac)" which takes a new paramter of type
>org.apache.common.collections.ObjectFactory (an Interface). It aids the use
>of this map as a cache (see below).
>
>I feel that the idea to extend the class with "createReference()"
>overimplemented is a bad one, and have removed the functionality. Among
>other things, it is difficult to see how this would be implemented here
>without unnecessary complications - considering the functionality is
>questionable (to extend SoftRefHashMap to actually be a WeakRefHashMap?) I
>think that this is justified.
>
>I include two files org.apache.commons.collections.SoftRefHashMap and
>org.apache.commons.collections.ObjectFactory.
>
>The idea of Object factory is so that the Map can be used effectively as a
>Cache; suppose that a User wishes to store (expensively retrieved) database
>entries in the Map which must always be retrieved (whether the referent
>value has since been GC-ed or not). The user code looks like
>
>//
>public UserObject apiGetUser(final String userId) {
>  return (UserObject) map.getIfAbsentPut(userId, new ObjectFactory() {
>     public Object create() {
>         //expensive database access to return the
>         //required Object
>         Object user = expensiveDBAccess( userId );
>         return user;
>     }
>  });
>}
>//
>
>The ObjectFactory.create() will only be invoked (and the expensive 
>operation
>executed) should the value have been cleared from the Map (ie. GC-d). The
>factory method will be invoked and the Object stored in the map for the 
>key.
>
>Anyway here is the source code...
>
>
>*****************************************
>
>
>/**
>*
>* The Apache Software License, Version 1.1
>*
>* Copyright (c) 1999-2002 The Apache Software Foundation.  All rights
>* reserved.
>*
>* Redistribution and use in source and binary forms, with or without
>* modification, are permitted provided that the following conditions
>* are met:
>*
>* 1. Redistributions of source code must retain the above copyright
>*    notice, this list of conditions and the following disclaimer.
>*
>* 2. 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.
>*
>* 3. The end-user documentation included with the redistribution, if
>*    any, must include the following acknowlegement:
>*       "This product includes software developed by the
>*        Apache Software Foundation (http://www.apache.org/)."
>*    Alternately, this acknowlegement may appear in the software itself,
>*    if and wherever such third-party acknowlegements normally appear.
>*
>* 4. The names "The Jakarta Project", "Commons", and "Apache Software
>*    Foundation" must not be used to endorse or promote products derived
>*    from this software without prior written permission. For written
>*    permission, please contact apache@apache.org.
>*
>* 5. Products derived from this software may not be called "Apache"
>*    nor may "Apache" appear in their names without prior written
>*    permission of the Apache Group.
>*
>* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR
>* ITS 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.
>* ====================================================================
>*
>* This software consists of voluntary contributions made by many
>* individuals on behalf of the Apache Software Foundation.  For more
>* information on the Apache Software Foundation, please see
>* <http://www.apache.org/>.
>*
>*/
>package org.apache.commons.collections;
>
>/**
>* This interface provides an abstract Object factory.
>* @author Chris Marshall
>* @version 1.0
>*/
>
>public interface ObjectFactory {
>  /**
>   * return an Object.
>   */
>  public Object create();
>}
>
>*******************************************************************
>
>
>/*
>*
>* The Apache Software License, Version 1.1
>*
>* Copyright (c) 1999-2002 The Apache Software Foundation.  All rights
>* reserved.
>*
>* Redistribution and use in source and binary forms, with or without
>* modification, are permitted provided that the following conditions
>* are met:
>*
>* 1. Redistributions of source code must retain the above copyright
>*    notice, this list of conditions and the following disclaimer.
>*
>* 2. 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.
>*
>* 3. The end-user documentation included with the redistribution, if
>*    any, must include the following acknowlegement:
>*       "This product includes software developed by the
>*        Apache Software Foundation (http://www.apache.org/)."
>*    Alternately, this acknowlegement may appear in the software itself,
>*    if and wherever such third-party acknowlegements normally appear.
>*
>* 4. The names "The Jakarta Project", "Commons", and "Apache Software
>*    Foundation" must not be used to endorse or promote products derived
>*    from this software without prior written permission. For written
>*    permission, please contact apache@apache.org.
>*
>* 5. Products derived from this software may not be called "Apache"
>*    nor may "Apache" appear in their names without prior written
>*    permission of the Apache Group.
>*
>* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR
>* ITS 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.
>* ====================================================================
>*
>* This software consists of voluntary contributions made by many
>* individuals on behalf of the Apache Software Foundation.  For more
>* information on the Apache Software Foundation, please see
>* <http://www.apache.org/>.
>*
>*/
>package org.apache.commons.collections;
>
>import java.lang.ref.*;
>import java.lang.reflect.*;
>import java.util.*;
>
>/** <p>
>  * HashMap with SoftReference links to values which allows the values of
>the Map
>  * to be garbage collected by the JVM if it becomes low on memory.
>  * Derive from this class and
>  * override the factory method <code>createReference()</code> method to
>make
>  * a Map wrapped in other types of Reference.
>  * </p>
>  *
>  * <p>
>  * A synchronized version can be obtained with:
>  * <code>Collections.synchronizedMap( theMapToSynchronize )</code>
>  * </p>
>  *
>  * <p>
>  * <b>WARNING</b> the values() and entrySet() methods require optimisation
>  * like the standard {@link HashMap} implementations so that iteration
>  * over this Map is efficient.
>  * </p>
>  *
>  * @author  James.Dodd
>  * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
>  */
>public class SoftRefHashMap implements Map {
>
>    /** The wrapped HashMap */
>    private Map hashMap = new HashMap();
>    private ReferenceQueue queue = new ReferenceQueue();
>
>    // Inner Classes
>    // 
>---------------------------------------------------------------------
>
>    /**
>	* Internal factory class to return <tt>Reference</tt>
>	* implementation.
>	*/
>   private static class RefFactory {
>	  private static Reference reference(
>		 Object o,
>		 Object key,
>		 ReferenceQueue queue) {
>		 return SoftKey.create(o, key, queue);
>	  }
>   }
>/**
>	* Internal extension of <tt>SoftReference</tt>
>	* to be stored as the <tt>value</tt> in the
>	* underlying map.
>	*/
>   static private class SoftKey extends SoftReference {
>	  private int hash;
>	  private Object key;
>	  /* Hashcode of key, stored here since the key
>	  may be tossed by the GC */
>
>	  private SoftKey(Object k, Object key, ReferenceQueue queue) {
>		 super(k, queue);
>		 this.key = key;
>		 hash = k.hashCode();
>	  }
>
>	  private static SoftKey create(Object k, Object key, ReferenceQueue 
>queue)
>{
>		 if (k == null)
>			return null;
>		 else
>			return new SoftKey(k, key, queue);
>	  }
>
>	  /* A SoftKey is equal to another WeakKey iff they both refer to objects
>	  that are, in turn, equal according to their own equals methods */
>	  public boolean equals(Object o) {
>		 if (this == o)
>			return true;
>		 if (!(o instanceof SoftKey))
>			return false;
>		 Object t = this.get();
>		 Object u = ((SoftKey) o).get();
>		 if ((t == null) || (u == null))
>			return false;
>		 if (t == u)
>			return true;
>		 return t.equals(u);
>	  }
>	  public Object getKey() {
>		 return this.key;
>	  }
>
>	  public int hashCode() {
>		 return hash;
>	  }
>
>	  public String toString() {
>		 return "SoftKey(" + get() + ")";
>	  }
>   }
>
>   /**
>	* Internal implementation of <tt>Map.Entry</tt>
>	* for presentation through the <tt>EntryIterator</tt>
>	*/
>   private class SoftKeyEntry implements Map.Entry {
>	  private Object key;
>	  private Reference value;
>
>	  private Map.Entry underlying;
>	  private SoftKeyEntry(Object key, Reference value) {
>		 this.key = key;
>		 this.value = value;
>	  }
>
>	  private SoftKeyEntry(Map.Entry e) {
>		 this.underlying = e;
>		 this.key = e.getKey();
>		 if (e instanceof SoftKeyEntry)
>			this.value = ((SoftKeyEntry) e).getRef();
>		 else if (e.getValue() instanceof Reference)
>			this.value = (Reference) e.getValue();
>		 else
>		 	this.value = RefFactory.reference(e.getValue(), e.getKey(), queue);
>	  }
>	  public Object getKey() {
>		 return this.key;
>	  }
>	  public Object getValue() {
>		 return this.value.get();
>	  }
>	  public Object setValue(Object o) {
>		 if (underlying != null)
>			underlying.setValue(o);
>		 Object oldValue = value.get();
>		 this.value = RefFactory.reference(o, getKey(), queue);
>		 return oldValue;
>	  }
>
>	  private Reference getRef() {
>		 return this.value;
>	  }
>   }
>
>   /**
>	* Internal <tt>Iterator</tt> representation to iterate
>	* Through the underlying <tt>values()</tt>
>	*/
>   private class ValueIterator implements Iterator {
>	  private Iterator iterator;
>	  private ValueIterator() {
>		 this.iterator = getMap().values().iterator();
>	  }
>	  public boolean hasNext() {
>		 return this.iterator.hasNext();
>	  }
>
>	  public Object next() {
>		 if (!this.iterator.hasNext())
>			throw new NoSuchElementException();
>		 Object o = null;
>		 while (o == null && this.iterator.hasNext()) {
>			Reference ref = (Reference) this.iterator.next();
>			if ((o = ref.get()) == null)
>			   this.iterator.remove();
>		 }
>		 return o;
>	  }
>
>	  public void remove() {
>		 this.iterator.remove();
>	  }
>   }
>
>   /**
>	* Internal <tt>Iterator</tt> representation to
>	* iterate through the underlying <tt>entrySet()</tt>.
>	*/
>   private class EntryIterator implements Iterator {
>	  private Iterator iterator;
>	  private EntryIterator() {
>		 this.iterator = getMap().entrySet().iterator();
>	  }
>
>	  public boolean hasNext() {
>		 return this.iterator.hasNext();
>	  }
>
>	  public Object next() {
>		 if (!this.iterator.hasNext())
>			throw new NoSuchElementException();
>		 Map.Entry entry = null;
>		 while (entry == null && this.iterator.hasNext()) {
>			Map.Entry e = (Map.Entry) this.iterator.next();
>			Reference ref = (Reference) e.getValue();
>			if (ref.get() == null)
>			   this.iterator.remove();
>			else
>			   entry = new SoftKeyEntry(e);
>		 }
>		 return entry;
>	  }
>
>	  public void remove() {
>		 this.iterator.remove();
>	  }
>   }
>    public SoftRefHashMap() {
>    }
>
>
>    /**
>     * Removes References that have had their referents garbage collected
>     * @deprecated
>     */
>    public void purge() {
>        processQueue();
>    }
>
>    /**
>     * Retrieves the referent of the Referenced value
>     * @param key The key with which to retrieve the value
>     */
>    public Object get(Object key) {
>	SoftReference ref = (SoftReference) getMap().get(key);
>	if (ref == null)
>		return null;
>	Object o = null;
>	if ((o = ref.get()) == null)
>		remove(key);
>	return o;
>    }
>
>    /**
>     * Adds a key-value mapping, wrapping the value in a Reference
>     */
>    public Object put(Object key, Object value) {
>	processQueue();
>	return getMap().put(key, RefFactory.reference(value, key, queue));
>    }
>
>    /**
>     * Process the reference queue to clean up.
>     * Creation date: (07/05/02 10:13:59)
>     */
>    private void processQueue() {
>       Reference r = null;
>       while ((r = queue.poll()) != null)
>              getMap().remove(((SoftKey) r).getKey());
>    }
>    /**
>      * Returns a collection of the Referenced values
>      */
>    public java.util.Collection values() {
>            return new AbstractCollection() {
>                    public int size() {
>                            return getMap().size();
>                    }
>
>                    public Iterator iterator() {
>                            return new ValueIterator();
>
>                    }
>            };
>
>    }
>
>    /**
>     * Returns the value to which this map maps the specified key.  If the
>     * map does not contain the key, or if the value has been cleared by 
>the
>     * GarbageCollector, the supplied <tt>ObjectFactory</tt> is used to
>place
>     * a new value in the map foir the key. This new value is returned.
>     *
>     * @param key key whose associated value is to be returned.
>     * @param objectFactory factory to create the Object with which to put
>     *			the <tt>Map</tt>
>     * @return the value to which this map maps the specified key, or
>     *	       <tt>objectFactory.create()</tt> if the map contains no
>     *		   mapping (or the mapping has been cleared) for this key.
>     *
>     * @throws ClassCastException if the key is of an inappropriate type 
>for
>     * 		   this map.
>     * @throws NullPointerException key is <tt>null</tt> and this map does
>not
>     *		  not permit <tt>null</tt> keys.
>     * @throws Exception if the <tt>ObjectFactory</tt>'s
>     *		   <tt>create()</tt> method throws an Exception
>     *
>     * @see #containsKey(Object)
>     */
>    public Object getIfAbsentPut(Object key,
>org.apache.commons.collections.ObjectFactory objectFactory)
>       throws Exception {
>       Object o = get(key);
>       if (o == null)
>              put(key, o = objectFactory.create());
>       return o;
>    }
>    /**
>     * Answers whether the argument is in the domain of the mappings
>     */
>    public boolean containsKey( Object key ) {
>        return getMap().containsKey( key );
>    }
>
>    /**
>     * Answers whether the argument is a Referenced value
>     */
>    public boolean containsValue( Object value ) {
>        Collection values = (Collection) getMap().values();
>        if ( values == null ) {
>            return false;
>        }
>        for ( Iterator i = values.iterator(); i.hasNext(); ) {
>            Reference ref = (Reference) i.next();
>            if ( ref == null ) {
>                continue;
>            }
>            Object target = ref.get();
>            if ( target == value ) {
>                return true;
>            }
>        }
>        return false;
>    }
>
>    /**
>      * Put all of the mappings in the argument into this wrapped map
>      */
>    public void putAll(java.util.Map map) {
>        if ( map == null || map.size() == 0 ) {
>            return;
>        }
>        for (Iterator it = map.entrySet().iterator(); it.hasNext();) {
>	    Map.Entry e = (Map.Entry) it.next();
>	    put(e.getKey(), e.getValue());
>        }
>    }
>
>    /**
>      * Returns a set view of the mappings in the wrapped map
>      */
>    public java.util.Set entrySet() {
>	return new AbstractSet() {
>
>		public int size() {
>			return getMap().size();
>		}
>
>		public Iterator iterator() {
>			return new EntryIterator();
>		}
>	};
>    }
>
>    /**
>      * Removes a mapping from this map
>      */
>    public Object remove(Object key) {
>	SoftReference ref = (SoftReference) getMap().remove(key);
>        if (ref != null) {
>	    return ref.get();
>        }
>        return null;
>    }
>
>    /**
>      * Clears all  mappings
>      */
>    public void clear() {
>        getMap().clear();
>    }
>
>    /**
>      * Calculates the hash code for this map
>      */
>    public int hashCode() {
>        return getMap().hashCode();
>    }
>
>    /**
>      * Returns the domain of the mappings
>      */
>    public Set keySet() {
>        return getMap().keySet();
>    }
>
>    /**
>      * Answers whether there are any mappings
>      */
>    public boolean isEmpty() {
>        return getMap().isEmpty();
>    }
>
>    /**
>      * Answers whether this map and the argument are 'the same'
>      */
>    public boolean equals( final Object object ) {
>        return getMap().equals( object );
>    }
>
>    /**
>      * Returns the number of mappings in this map
>      */
>    public int size() {
>        return getMap().size();
>    }
>
>
>    /**
>     * Retrieves the wrapped HashMap
>     * @return The wrapped HashMap
>     */
>    protected Map getMap() {
>        return hashMap;
>    }
>}
>
>_________________________________________________________________
>Chat with friends online, try MSN Messenger: http://messenger.msn.com
>
>
>--
>To unsubscribe, e-mail:   
><mailto:commons-dev-unsubscribe@jakarta.apache.org>
>For additional commands, e-mail: 
><mailto:commons-dev-help@jakarta.apache.org>
>




_________________________________________________________________
Send and receive Hotmail on your mobile device: http://mobile.msn.com


--
To unsubscribe, e-mail:   <mailto:commons-dev-unsubscribe@jakarta.apache.org>
For additional commands, e-mail: <mailto:commons-dev-help@jakarta.apache.org>


Mime
View raw message