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 {
/** 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 map will purged of cleared entries.
* The maximum number of times put() or remove() can be called before
* 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 */
private ReferenceQueue queue = new ReferenceQueue();
/* 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
@@ -87,17 +91,6 @@ public final class WeakHashtable extends Hashtable {
*/
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
@@ -108,33 +101,12 @@ public final class WeakHashtable extends Hashtable {
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
*/
public Enumeration elements() {
purge();
final Enumeration enum = super.elements();
return new Enumeration() {
public boolean hasMoreElements() {
return enum.hasMoreElements();
}
public Object nextElement() {
Referenced nextReference = (Referenced) enum.nextElement();
return nextReference.getValue();
}
};
return super.elements();
}
/**
@@ -148,8 +120,7 @@ public final class WeakHashtable extends Hashtable {
Map.Entry entry = (Map.Entry) it.next();
Referenced referencedKey = (Referenced) entry.getKey();
Object key = referencedKey.getValue();
Referenced referencedValue = (Referenced) entry.getValue();
Object value = referencedValue.getValue();
Object value = entry.getValue();
if (key != null) {
Entry dereferencedEntry = new Entry(key, value);
unreferencedEntries.add(dereferencedEntry);
@@ -163,13 +134,8 @@ public final class WeakHashtable extends Hashtable {
*/
public Object get(Object key) {
// for performance reasons, no purge
Object result = null;
Referenced referenceKey = new Referenced(key);
Referenced referencedValue = (Referenced) super.get(referenceKey);
if (referencedValue != null) {
result = referencedValue.getValue();
}
return result;
return super.get(referenceKey);
}
/**
@@ -177,13 +143,13 @@ public final class WeakHashtable extends Hashtable {
*/
public Enumeration keys() {
purge();
final Enumeration enum = super.keys();
final Enumeration enumer = super.keys();
return new Enumeration() {
public boolean hasMoreElements() {
return enum.hasMoreElements();
return enumer.hasMoreElements();
}
public Object nextElement() {
Referenced nextReference = (Referenced) enum.nextElement();
Referenced nextReference = (Referenced) enumer.nextElement();
return nextReference.getValue();
}
};
@@ -220,19 +186,19 @@ public final class WeakHashtable extends Hashtable {
}
// for performance reasons, only purge every
// MAX_PUTS_BEFORE_PURGE times
if (putCount++ > MAX_PUTS_BEFORE_PURGE) {
// MAX_CHANGES_BEFORE_PURGE times
if (changeCount++ > MAX_CHANGES_BEFORE_PURGE) {
purge();
putCount = 0;
changeCount = 0;
}
// do a partial purge more often
else if ((changeCount % PARTIAL_PURGE_COUNT) == 0) {
purgeOne();
}
Object result = null;
Referenced keyRef = new Referenced(key, value, queue);
Referenced valueRef = new Referenced(value);
Referenced lastValue = (Referenced) super.put(keyRef, valueRef);
if (lastValue != null) {
result = lastValue.getValue();
}
return result;
Referenced keyRef = new Referenced(key, queue);
return super.put(keyRef, value);
}
/**
@@ -253,22 +219,23 @@ public final class WeakHashtable extends Hashtable {
*/
public Collection values() {
purge();
Collection referencedValues = 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;
return super.values();
}
/**
*@see Hashtable
*/
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));
}
@@ -309,12 +276,28 @@ public final class WeakHashtable extends Hashtable {
* Purges all entries whose wrapped keys
* have been garbage collected.
*/
private synchronized void purge() {
private void purge() {
synchronized (queue) {
WeakKey key;
while ((key = (WeakKey) queue.poll()) != null) {
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 */
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>
*/
private Referenced(Object key, Object value, ReferenceQueue queue) {
reference = new WeakKey(key, value, queue, this);
private Referenced(Object key, ReferenceQueue queue) {
reference = new WeakKey(key, queue, this);
// Calc a permanent hashCode so calls to Hashtable.remove()
// work if the WeakReference has been cleared
hashCode = key.hashCode();
@@ -438,37 +421,17 @@ public final class WeakHashtable extends Hashtable {
*/
private final static class WeakKey extends WeakReference {
private final Object hardValue;
private final Referenced referenced;
private WeakKey(Object key,
Object value,
ReferenceQueue queue,
Referenced referenced) {
super(key, queue);
hardValue = value;
this.referenced = referenced;
}
private Referenced getReferenced() {
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;
//}
}
}