Java Tutorial/Collections/Reference
An implementation of Set that manages a map of soft references to the set values.
/*
* JBoss, Home of Professional Open Source
* Copyright 2005, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
/**
* An implementation of Set that manages a map of soft references to the set
* values. The map is keyed by the value hashCode and so this is only useful for
* value whose hashCode is a valid identity representation (String, primative
* wrappers, etc).
*
* @author Scott.Stark@jboss.org
* @version $Revision: 2787 $
*/
@SuppressWarnings("unchecked")
public class SoftSet implements Set {
private HashMap map = new HashMap();
/** The queue of garbage collected soft references */
private ReferenceQueue gcqueue = new ReferenceQueue();
static class ComparableSoftReference extends SoftReference {
private Integer key;
ComparableSoftReference(Integer key, Object o, ReferenceQueue q) {
super(o, q);
this.key = key;
}
Integer getKey() {
return key;
}
}
static class ComparableSoftReferenceIterator implements Iterator {
Iterator theIter;
ComparableSoftReferenceIterator(Iterator theIter) {
this.theIter = theIter;
}
public boolean hasNext() {
return theIter.hasNext();
}
public Object next() {
ComparableSoftReference csr = (ComparableSoftReference) theIter.next();
return csr.get();
}
public void remove() {
theIter.remove();
}
}
/**
*
*/
public SoftSet() {
}
public int size() {
processQueue();
return map.size();
}
public boolean isEmpty() {
processQueue();
return map.isEmpty();
}
public boolean contains(Object o) {
processQueue();
Integer key = new Integer(o.hashCode());
boolean contains = map.containsKey(key);
return contains;
}
public Iterator iterator() {
processQueue();
Iterator theIter = map.values().iterator();
return new ComparableSoftReferenceIterator(theIter);
}
public Object[] toArray() {
processQueue();
return toArray(new Object[0]);
}
public Object[] toArray(Object[] a) {
processQueue();
int size = map.size();
Object[] array = {};
if (a.length >= size)
array = a;
Iterator iter = map.values().iterator();
int index = 0;
while (iter.hasNext()) {
ComparableSoftReference csr = (ComparableSoftReference) iter.next();
Object value = csr.get();
// Create the correct array type
if (array.length == 0) {
if (value == null) {
index++;
continue;
}
Array.newInstance(value.getClass(), size);
}
array[index] = value;
index++;
}
return array;
}
public boolean add(Object o) {
processQueue();
Integer key = new Integer(o.hashCode());
ComparableSoftReference sr = new ComparableSoftReference(key, o, gcqueue);
return map.put(key, sr) == null;
}
public boolean remove(Object o) {
processQueue();
Integer key = new Integer(o.hashCode());
return map.remove(key) != null;
}
public boolean containsAll(Collection c) {
processQueue();
Iterator iter = c.iterator();
boolean contains = true;
while (iter.hasNext()) {
Object value = iter.next();
Integer key = new Integer(value.hashCode());
contains &= map.containsKey(key);
}
return contains;
}
public boolean addAll(Collection c) {
processQueue();
Iterator iter = c.iterator();
boolean added = false;
while (iter.hasNext()) {
Object value = iter.next();
Integer key = new Integer(value.hashCode());
ComparableSoftReference sr = new ComparableSoftReference(key, value, gcqueue);
added |= map.put(key, sr) == null;
}
return added;
}
public boolean retainAll(Collection c) {
Iterator iter = iterator();
boolean removed = false;
while (iter.hasNext()) {
Object value = iter.next();
if (c.contains(value) == false) {
iter.remove();
removed = true;
}
}
return removed;
}
public boolean removeAll(Collection c) {
processQueue();
Iterator iter = c.iterator();
boolean removed = false;
while (iter.hasNext()) {
Object value = iter.next();
removed |= remove(value);
}
return removed;
}
public void clear() {
while (gcqueue.poll() != null)
;
map.clear();
}
public boolean equals(Object o) {
return map.equals(o);
}
public int hashCode() {
return map.hashCode();
}
/**
* Iterate through the gcqueue for for any cleared reference, remove the
* associated value from the underlying set.
*/
private void processQueue() {
ComparableSoftReference cr;
while ((cr = (ComparableSoftReference) gcqueue.poll()) != null) {
map.remove(cr.getKey());
}
}
}
Cache based on SoftReference
/* Copyright 2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Revised from xmlbeans
import java.util.HashMap;
import java.lang.ref.SoftReference;
/**
* @author Cezar Andrei (cezar.andrei at bea.ru)
* Date: Apr 26, 2005
*/
public class SoftCache
{
private HashMap map = new HashMap();
public Object get(Object key)
{
SoftReference softRef = (SoftReference)map.get(key);
if (softRef==null)
return null;
return softRef.get();
}
public Object put(Object key, Object value)
{
SoftReference softRef = (SoftReference)map.put(key, new SoftReference(value));
if (softRef==null)
return null;
Object oldValue = softRef.get();
softRef.clear();
return oldValue;
}
public Object remove(Object key)
{
SoftReference softRef = (SoftReference)map.remove(key);
if (softRef==null)
return null;
Object oldValue = softRef.get();
softRef.clear();
return oldValue;
}
}
WeakReference list uses java.lang.ref.WeakReferences to store its contents.
/**
*
* JFreeReport : a free Java reporting library
*
*
* Project Info: http://reporting.pentaho.org/
*
* (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
*
* This library is free software; you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Foundation;
* either version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* [Java is a trademark or registered trademark of Sun Microsystems, Inc.
* in the United States and other countries.]
*
* ------------
* WeakReferenceList.java
* ------------
* (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
*/
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
/**
* The WeakReference list uses <code>java.lang.ref.WeakReference</code>s to store its contents. In contrast to the
* WeakHashtable, this list knows how to restore missing content, so that garbage collected elements can be restored
* when they are accessed.
* <p/>
* By default this list can contain 25 elements, where the first element is stored using a strong reference, which is
* not garbage collected.
* <p/>
* Restoring the elements is not implemented, concrete implementations will have to override the
* <code>restoreChild(int)</code> method. The <code>getMaxChildCount</code> method defines the maxmimum number of
* children in the list. When more than <code>maxChildCount</code> elements are contained in this list, add will always
* return false to indicate that adding the element failed.
* <p/>
* To customize the list, override createReference to create a different kind of reference.
* <p/>
* This list is able to add or replace elements, but inserting or removing of elements is not possible.
*
* @author Thomas Morgner
*/
public abstract class WeakReferenceList implements Serializable, Cloneable
{
/**
* The master element.
*/
private Object master;
/**
* Storage for the references.
*/
private transient Reference[] childs;
/**
* The current number of elements.
*/
private int size;
/**
* The maximum number of elements.
*/
private final int maxChilds;
/**
* Creates a new weak reference list. The storage of the list is limited to getMaxChildCount() elements.
*
* @param maxChildCount the maximum number of elements.
*/
protected WeakReferenceList(final int maxChildCount)
{
this.maxChilds = maxChildCount;
this.childs = new Reference[maxChildCount - 1];
}
/**
* Returns the maximum number of children in this list.
*
* @return the maximum number of elements in this list.
*/
protected final int getMaxChildCount()
{
return maxChilds;
}
/**
* Returns the master element of this list. The master element is the element stored by a strong reference and cannot
* be garbage collected.
*
* @return the master element
*/
protected Object getMaster()
{
return master;
}
/**
* Attempts to restore the child stored on the given index.
*
* @param index the index.
* @return null if the child could not be restored or the restored child.
*/
protected abstract Object restoreChild(int index);
/**
* Returns the child stored at the given index. If the child has been garbage collected, it gets restored using the
* restoreChild function.
*
* @param index the index.
* @return the object.
*/
public Object get(final int index)
{
if (isMaster(index))
{
return master;
}
else
{
final Reference ref = childs[getChildPos(index)];
if (ref == null)
{
throw new IllegalStateException("State: " + index);
}
Object ob = ref.get();
if (ob == null)
{
ob = restoreChild(index);
childs[getChildPos(index)] = createReference(ob);
}
return ob;
}
}
/**
* Replaces the child stored at the given index with the new child which can be null.
*
* @param report the object.
* @param index the index.
*/
public void set(final Object report, final int index)
{
if (isMaster(index))
{
master = report;
}
else
{
childs[getChildPos(index)] = createReference(report);
}
}
/**
* Creates a new reference for the given object.
*
* @param o the object.
* @return a WeakReference for the object o without any ReferenceQueue attached.
*/
private Reference createReference(final Object o)
{
return new WeakReference(o);
}
/**
* Adds the element to the list. If the maximum size of the list is exceeded, this function returns false to indicate
* that adding failed.
*
* @param rs the object.
* @return true, if the object was successfully added to the list, false otherwise
*/
public boolean add(final Object rs)
{
if (size == 0)
{
master = rs;
size = 1;
return true;
}
else
{
if (size < getMaxChildCount())
{
childs[size - 1] = createReference(rs);
size++;
return true;
}
else
{
// was not able to add this to this list, maximum number of entries reached.
return false;
}
}
}
/**
* Returns true, if the given index denotes a master index of this list.
*
* @param index the index.
* @return true if the index is a master index.
*/
protected boolean isMaster(final int index)
{
return index % getMaxChildCount() == 0;
}
/**
* Returns the internal storage position for the child.
*
* @param index the index.
* @return the internal storage index.
*/
protected int getChildPos(final int index)
{
return index % getMaxChildCount() - 1;
}
/**
* Returns the size of the list.
*
* @return the size.
*/
public int getSize()
{
return size;
}
/**
* Serialisation support. The transient child elements were not saved.
*
* @param in the input stream.
* @throws IOException if there is an I/O error.
* @throws ClassNotFoundException if a serialized class is not defined on this system.
*/
private void readObject(final ObjectInputStream in)
throws IOException, ClassNotFoundException
{
in.defaultReadObject();
childs = new Reference[getMaxChildCount() - 1];
for (int i = 0; i < childs.length; i++)
{
childs[i] = createReference(null);
}
}
/**
* Creates and returns a copy of this object. The precise meaning of "copy" may depend on the class of the object.
* The general intent is that, for any object <tt>x</tt>, the expression: <blockquote>
* <pre>
* x.clone() != x</pre></blockquote>
* will be true, and that the expression: <blockquote>
* <pre>
* x.clone().getClass() == x.getClass()</pre></blockquote>
* will be <tt>true</tt>, but these are not absolute requirements. While it is typically the case that: <blockquote>
* <pre>
* x.clone().equals(x)</pre></blockquote>
* will be <tt>true</tt>, this is not an absolute requirement.
* <p/>
* By convention, the returned object should be obtained by calling <tt>super.clone</tt>. If a class and all of its
* superclasses (except <tt>Object</tt>) obey this convention, it will be the case that <tt>x.clone().getClass() ==
* x.getClass()</tt>.
* <p/>
* By convention, the object returned by this method should be independent of this object (which is being cloned). To
* achieve this independence, it may be necessary to modify one or more fields of the object returned by
* <tt>super.clone</tt> before returning it. Typically, this means copying any mutable objects that comprise the
* internal "deep structure" of the object being cloned and replacing the references to these objects with references
* to the copies. If a class contains only primitive fields or references to immutable objects, then it is usually
* the case that no fields in the object returned by <tt>super.clone</tt> need to be modified.
* <p/>
* The method <tt>clone</tt> for class <tt>Object</tt> performs a specific cloning operation. First, if the class of
* this object does not implement the interface <tt>Cloneable</tt>, then a <tt>CloneNotSupportedException</tt> is
* thrown. Note that all arrays are considered to implement the interface <tt>Cloneable</tt>. Otherwise, this method
* creates a new instance of the class of this object and initializes all its fields with exactly the contents of the
* corresponding fields of this object, as if by assignment; the contents of the fields are not themselves cloned.
* Thus, this method performs a "shallow copy" of this object, not a "deep copy" operation.
* <p/>
* The class <tt>Object</tt> does not itself implement the interface <tt>Cloneable</tt>, so calling the <tt>clone</tt>
* method on an object whose class is <tt>Object</tt> will result in throwing an exception at run time.
*
* @return a clone of this instance.
* @throws CloneNotSupportedException if the object"s class does not support the <code>Cloneable</code> interface.
* Subclasses that override the <code>clone</code> method can also throw this
* exception to indicate that an instance cannot be cloned.
* @see Cloneable
*/
protected Object clone()
throws CloneNotSupportedException
{
final WeakReferenceList list = (WeakReferenceList) super.clone();
list.childs = (Reference[]) childs.clone();
return list;
}
}