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 16:46:34 GMT



>From: "Michael A. Smith" <mas@apache.org>
>here's a quick review of your mail:
>
>Having the entrySet and values collections backed by the map is probably a
>good thing.  As for the purge stuff, I don't know what your motivation is

Well, the existing "purge()" method iterated through the whole Map checking 
each reference so see if it had been cleared. The new "purge()" method 
simply calls "processQueue()". This does not iterate through the whole Map, 
but instead uses the ReferenceQueue to directly get the references that have 
been cleared and remove them from the Map. This should be much quicker.

>here.  "performant" isn't in my trusty on-line dictionaries:
>Are you saying it has better performance?  That's probably good as well.

Yes, that is what I meant - I thought it was a generic IT term. Apologies.

>
> > >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).
>
>In my opinion, this is not appropriate to add to the SoftRefHashMap.

Fair point - I will remove it.

> > >I feel that the idea to extend the class with "createReference()"
> > >overimplemented is a bad one, and have removed the functionality. I
> > >think that this is justified.
>
>well, I don't.  You're removing functionality and if you just removed the
>method, you're breaking backwards compatibility.  what unnecessary
>complications are you talking about?  Just a possible misleading super
>class?  How else is this functionality questionable?

Backwardly compatible? The class is broken as it is! People whose code 
extends the current version doesn't work anyway...

The functionality is questionable not only because composition should be 
favoured over inheritance; it seems madness that I can extend a class called 
SoftRefHashMap to NOT be a SoftRefHashMap.

The extra "subclass-able" functionality CANNOT exist in my implementation 
--> specifically, the reference used im the Map must not only contain the 
value placed in the Map, but ALSO the KEY. This is essential and the 
Reference returned by createReference() does not enforce this (the 
workaround would have to be to enforce the Map to break at Runtime with a 
ClassCastException).

To be fair, given that my PATCH is actually a faithful implementation of the 
Map interface and the current class is not, I don't see the problem - you 
would be replacing a broken class that has some unnecessary and questionable 
functionality with a working version without it. Surely where someone has 
used a bad design in this instance, the problem can be rectified.

Alternatively, my code could be placed as another class 
(org.apache.commons.collections.FaithfulSoftRefHashMapThatWorks) (as I 
originally proposed). Here is the changed code (without ObjectFactory). I 
would welcome a discussion on my proposal...

Chris

/*
*
* 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();

                    }
            };

    }

    /**
     * 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;
    }

}




_________________________________________________________________
Get your FREE download of MSN Explorer at http://explorer.msn.com/intl.asp.


--
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