1
0

Improved housekeeping and simplified code for WeakHashTable. Issue#31286. Submitted by Brian Stansberry.

git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/logging/trunk@139062 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Robert Burrell Donkin
2004-12-15 10:35:19 +00:00
parent b4e92dcb66
commit e7c56e483f

View File

@@ -68,18 +68,22 @@ import java.util.*;
*/ */
public final class WeakHashtable extends Hashtable { public final class WeakHashtable extends Hashtable {
/** Empty array of <code>Entry</code>'s */
private static final Entry[] EMPTY_ENTRY_ARRAY = {};
/** /**
* The maximum number of times put() can be called before * The maximum number of times put() or remove() can be called before
* the map will purged of cleared entries. * the map will be purged of all cleared entries.
*/ */
public static final int MAX_PUTS_BEFORE_PURGE = 100; private static final int MAX_CHANGES_BEFORE_PURGE = 100;
/**
* The maximum number of times put() or remove() can be called before
* the map will be purged of one cleared entry.
*/
private static final int PARTIAL_PURGE_COUNT = 10;
/* ReferenceQueue we check for gc'd keys */ /* ReferenceQueue we check for gc'd keys */
private ReferenceQueue queue = new ReferenceQueue(); private ReferenceQueue queue = new ReferenceQueue();
/* Counter used to control how often we purge gc'd entries */ /* Counter used to control how often we purge gc'd entries */
private int putCount = 0; private int changeCount = 0;
/** /**
* Constructs a WeakHashtable with the Hashtable default * Constructs a WeakHashtable with the Hashtable default
@@ -87,17 +91,6 @@ public final class WeakHashtable extends Hashtable {
*/ */
public WeakHashtable() {} public WeakHashtable() {}
/**
*@see Hashtable
*/
public boolean contains(Object value) {
// purge should not be required
if (value instanceof Referenced) {
return super.contains(value);
}
Referenced referenced = new Referenced(value);
return super.contains(referenced);
}
/** /**
*@see Hashtable *@see Hashtable
@@ -108,33 +101,12 @@ public final class WeakHashtable extends Hashtable {
return super.containsKey(referenced); return super.containsKey(referenced);
} }
/**
*@see Hashtable
*/
public boolean containsValue(Object value) {
// purge should not be required
if (value instanceof Referenced) {
return super.contains(value);
}
Referenced referenced = new Referenced(value);
return super.containsValue(referenced);
}
/** /**
*@see Hashtable *@see Hashtable
*/ */
public Enumeration elements() { public Enumeration elements() {
purge(); purge();
final Enumeration enum = super.elements(); return super.elements();
return new Enumeration() {
public boolean hasMoreElements() {
return enum.hasMoreElements();
}
public Object nextElement() {
Referenced nextReference = (Referenced) enum.nextElement();
return nextReference.getValue();
}
};
} }
/** /**
@@ -148,8 +120,7 @@ public final class WeakHashtable extends Hashtable {
Map.Entry entry = (Map.Entry) it.next(); Map.Entry entry = (Map.Entry) it.next();
Referenced referencedKey = (Referenced) entry.getKey(); Referenced referencedKey = (Referenced) entry.getKey();
Object key = referencedKey.getValue(); Object key = referencedKey.getValue();
Referenced referencedValue = (Referenced) entry.getValue(); Object value = entry.getValue();
Object value = referencedValue.getValue();
if (key != null) { if (key != null) {
Entry dereferencedEntry = new Entry(key, value); Entry dereferencedEntry = new Entry(key, value);
unreferencedEntries.add(dereferencedEntry); unreferencedEntries.add(dereferencedEntry);
@@ -163,13 +134,8 @@ public final class WeakHashtable extends Hashtable {
*/ */
public Object get(Object key) { public Object get(Object key) {
// for performance reasons, no purge // for performance reasons, no purge
Object result = null;
Referenced referenceKey = new Referenced(key); Referenced referenceKey = new Referenced(key);
Referenced referencedValue = (Referenced) super.get(referenceKey); return super.get(referenceKey);
if (referencedValue != null) {
result = referencedValue.getValue();
}
return result;
} }
/** /**
@@ -177,13 +143,13 @@ public final class WeakHashtable extends Hashtable {
*/ */
public Enumeration keys() { public Enumeration keys() {
purge(); purge();
final Enumeration enum = super.keys(); final Enumeration enumer = super.keys();
return new Enumeration() { return new Enumeration() {
public boolean hasMoreElements() { public boolean hasMoreElements() {
return enum.hasMoreElements(); return enumer.hasMoreElements();
} }
public Object nextElement() { public Object nextElement() {
Referenced nextReference = (Referenced) enum.nextElement(); Referenced nextReference = (Referenced) enumer.nextElement();
return nextReference.getValue(); return nextReference.getValue();
} }
}; };
@@ -220,19 +186,19 @@ public final class WeakHashtable extends Hashtable {
} }
// for performance reasons, only purge every // for performance reasons, only purge every
// MAX_PUTS_BEFORE_PURGE times // MAX_CHANGES_BEFORE_PURGE times
if (putCount++ > MAX_PUTS_BEFORE_PURGE) { if (changeCount++ > MAX_CHANGES_BEFORE_PURGE) {
purge(); purge();
putCount = 0; changeCount = 0;
} }
// do a partial purge more often
else if ((changeCount % PARTIAL_PURGE_COUNT) == 0) {
purgeOne();
}
Object result = null; Object result = null;
Referenced keyRef = new Referenced(key, value, queue); Referenced keyRef = new Referenced(key, queue);
Referenced valueRef = new Referenced(value); return super.put(keyRef, value);
Referenced lastValue = (Referenced) super.put(keyRef, valueRef);
if (lastValue != null) {
result = lastValue.getValue();
}
return result;
} }
/** /**
@@ -253,22 +219,23 @@ public final class WeakHashtable extends Hashtable {
*/ */
public Collection values() { public Collection values() {
purge(); purge();
Collection referencedValues = super.values(); return super.values();
ArrayList unreferencedValues = new ArrayList();
for (Iterator it = referencedValues.iterator(); it.hasNext();) {
Referenced reference = (Referenced) it.next();
Object value = reference.getValue();
if (value != null) {
unreferencedValues.add(value);
}
}
return unreferencedValues;
} }
/** /**
*@see Hashtable *@see Hashtable
*/ */
public Object remove(Object key) { public Object remove(Object key) {
// for performance reasons, only purge every
// MAX_CHANGES_BEFORE_PURGE times
if (changeCount++ > MAX_CHANGES_BEFORE_PURGE) {
purge();
changeCount = 0;
}
// do a partial purge more often
else if ((changeCount % PARTIAL_PURGE_COUNT) == 0) {
purgeOne();
}
return super.remove(new Referenced(key)); return super.remove(new Referenced(key));
} }
@@ -309,12 +276,28 @@ public final class WeakHashtable extends Hashtable {
* Purges all entries whose wrapped keys * Purges all entries whose wrapped keys
* have been garbage collected. * have been garbage collected.
*/ */
private synchronized void purge() { private void purge() {
synchronized (queue) {
WeakKey key; WeakKey key;
while ((key = (WeakKey) queue.poll()) != null) { while ((key = (WeakKey) queue.poll()) != null) {
super.remove(key.getReferenced()); super.remove(key.getReferenced());
} }
} }
}
/**
* Purges one entry whose wrapped key
* has been garbage collected.
*/
private void purgeOne() {
synchronized (queue) {
WeakKey key = (WeakKey) queue.poll();
if (key != null) {
super.remove(key.getReferenced());
}
}
}
/** Entry implementation */ /** Entry implementation */
private final static class Entry implements Map.Entry { private final static class Entry implements Map.Entry {
@@ -383,8 +366,8 @@ public final class WeakHashtable extends Hashtable {
* *
* @throws NullPointerException if key is <code>null</code> * @throws NullPointerException if key is <code>null</code>
*/ */
private Referenced(Object key, Object value, ReferenceQueue queue) { private Referenced(Object key, ReferenceQueue queue) {
reference = new WeakKey(key, value, queue, this); reference = new WeakKey(key, queue, this);
// Calc a permanent hashCode so calls to Hashtable.remove() // Calc a permanent hashCode so calls to Hashtable.remove()
// work if the WeakReference has been cleared // work if the WeakReference has been cleared
hashCode = key.hashCode(); hashCode = key.hashCode();
@@ -438,37 +421,17 @@ public final class WeakHashtable extends Hashtable {
*/ */
private final static class WeakKey extends WeakReference { private final static class WeakKey extends WeakReference {
private final Object hardValue;
private final Referenced referenced; private final Referenced referenced;
private WeakKey(Object key, private WeakKey(Object key,
Object value,
ReferenceQueue queue, ReferenceQueue queue,
Referenced referenced) { Referenced referenced) {
super(key, queue); super(key, queue);
hardValue = value;
this.referenced = referenced; this.referenced = referenced;
} }
private Referenced getReferenced() { private Referenced getReferenced() {
return referenced; return referenced;
} }
/* Drop our hard reference to value if we've been cleared
* by the gc.
*
* Testing shows that with key objects like ClassLoader
* that don't override hashCode(), get() is never
* called once the key is in a Hashtable.
* So, this method override is commented out.
*/
//public Object get() {
// Object result = super.get();
// if (result == null) {
// // We've been cleared, so drop our hard reference to value
// hardValue = null;
// }
// return result;
//}
} }
} }