[COLLECTIONS-696] AbstractReferenceMap made easier for subclassing; PR
#51.
This commit is contained in:
parent
ad442e3c73
commit
2374711606
|
@ -400,13 +400,15 @@ public abstract class AbstractReferenceMap<K, V> extends AbstractHashedMap<K, V>
|
||||||
HashEntry<K, V> previous = null;
|
HashEntry<K, V> previous = null;
|
||||||
HashEntry<K, V> entry = data[index];
|
HashEntry<K, V> entry = data[index];
|
||||||
while (entry != null) {
|
while (entry != null) {
|
||||||
if (((ReferenceEntry<K, V>) entry).purge(ref)) {
|
ReferenceEntry<K, V> refEntry = (ReferenceEntry<K, V>) entry;
|
||||||
|
if (refEntry.purge(ref)) {
|
||||||
if (previous == null) {
|
if (previous == null) {
|
||||||
data[index] = entry.next;
|
data[index] = entry.next;
|
||||||
} else {
|
} else {
|
||||||
previous.next = entry.next;
|
previous.next = entry.next;
|
||||||
}
|
}
|
||||||
this.size--;
|
this.size--;
|
||||||
|
refEntry.onPurge();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
previous = entry;
|
previous = entry;
|
||||||
|
@ -721,12 +723,18 @@ public abstract class AbstractReferenceMap<K, V> extends AbstractHashedMap<K, V>
|
||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the callback for custom "after purge" logic
|
||||||
|
*/
|
||||||
|
protected void onPurge() {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Purges the specified reference
|
* Purges the specified reference
|
||||||
* @param ref the reference to purge
|
* @param ref the reference to purge
|
||||||
* @return true or false
|
* @return true or false
|
||||||
*/
|
*/
|
||||||
boolean purge(final Reference<?> ref) {
|
protected boolean purge(final Reference<?> ref) {
|
||||||
boolean r = parent.keyType != ReferenceStrength.HARD && key == ref;
|
boolean r = parent.keyType != ReferenceStrength.HARD && key == ref;
|
||||||
r = r || parent.valueType != ReferenceStrength.HARD && value == ref;
|
r = r || parent.valueType != ReferenceStrength.HARD && value == ref;
|
||||||
if (r) {
|
if (r) {
|
||||||
|
@ -736,7 +744,7 @@ public abstract class AbstractReferenceMap<K, V> extends AbstractHashedMap<K, V>
|
||||||
if (parent.valueType != ReferenceStrength.HARD) {
|
if (parent.valueType != ReferenceStrength.HARD) {
|
||||||
((Reference<?>) value).clear();
|
((Reference<?>) value).clear();
|
||||||
} else if (parent.purgeValues) {
|
} else if (parent.purgeValues) {
|
||||||
value = null;
|
nullValue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
|
@ -750,6 +758,13 @@ public abstract class AbstractReferenceMap<K, V> extends AbstractHashedMap<K, V>
|
||||||
protected ReferenceEntry<K, V> next() {
|
protected ReferenceEntry<K, V> next() {
|
||||||
return (ReferenceEntry<K, V>) next;
|
return (ReferenceEntry<K, V>) next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method can be overriden to provide custom logic to purge value
|
||||||
|
*/
|
||||||
|
protected void nullValue() {
|
||||||
|
value = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
|
@ -1073,4 +1088,13 @@ public abstract class AbstractReferenceMap<K, V> extends AbstractHashedMap<K, V>
|
||||||
protected boolean isKeyType(final ReferenceStrength type) {
|
protected boolean isKeyType(final ReferenceStrength type) {
|
||||||
return this.keyType == type;
|
return this.keyType == type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provided protected read-only access to the value type.
|
||||||
|
* @param type the type to check against.
|
||||||
|
* @return true if valueType has the specified type
|
||||||
|
*/
|
||||||
|
protected boolean isValueType(final ReferenceStrength type) {
|
||||||
|
return this.valueType == type;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,15 +21,20 @@ import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.ObjectInputStream;
|
import java.io.ObjectInputStream;
|
||||||
import java.io.ObjectOutputStream;
|
import java.io.ObjectOutputStream;
|
||||||
|
import java.io.Serializable;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.function.Consumer;
|
||||||
import junit.framework.Test;
|
|
||||||
|
|
||||||
import org.apache.commons.collections4.BulkTest;
|
import org.apache.commons.collections4.BulkTest;
|
||||||
|
import org.apache.commons.collections4.map.AbstractHashedMap.HashEntry;
|
||||||
|
import org.apache.commons.collections4.map.AbstractReferenceMap.ReferenceEntry;
|
||||||
import org.apache.commons.collections4.map.AbstractReferenceMap.ReferenceStrength;
|
import org.apache.commons.collections4.map.AbstractReferenceMap.ReferenceStrength;
|
||||||
|
|
||||||
|
import junit.framework.Test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for ReferenceMap.
|
* Tests for ReferenceMap.
|
||||||
*
|
*
|
||||||
|
@ -255,6 +260,40 @@ public class ReferenceMapTest<K, V> extends AbstractIterableMapTest<K, V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testCustomPurge() {
|
||||||
|
List<Integer> expiredValues = new ArrayList<>();
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final Consumer<Integer> consumer = (Consumer<Integer> & Serializable) v -> expiredValues.add(v);
|
||||||
|
final Map<Integer, Integer> map = new ReferenceMap<Integer, Integer>(ReferenceStrength.WEAK, ReferenceStrength.HARD, false) {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ReferenceEntry<Integer, Integer> createEntry(HashEntry<Integer, Integer> next, int hashCode, Integer key, Integer value) {
|
||||||
|
return new AccessibleEntry<>(this, next, hashCode, key, value, consumer);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
for (int i = 100000; i < 100010; i++) {
|
||||||
|
map.put(Integer.valueOf(i), Integer.valueOf(i));
|
||||||
|
}
|
||||||
|
int iterations = 0;
|
||||||
|
int bytz = 2;
|
||||||
|
while (true) {
|
||||||
|
System.gc();
|
||||||
|
if (iterations++ > 50 || bytz < 0) {
|
||||||
|
fail("Max iterations reached before resource released.");
|
||||||
|
}
|
||||||
|
map.isEmpty();
|
||||||
|
if (!expiredValues.isEmpty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// create garbage:
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
final byte[] b = new byte[bytz];
|
||||||
|
bytz = bytz * 2;
|
||||||
|
}
|
||||||
|
assertFalse("Value should be stored", expiredValues.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test whether after serialization the "data" HashEntry array is the same size as the original.<p>
|
* Test whether after serialization the "data" HashEntry array is the same size as the original.<p>
|
||||||
*
|
*
|
||||||
|
@ -292,4 +331,21 @@ public class ReferenceMapTest<K, V> extends AbstractIterableMapTest<K, V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class AccessibleEntry<K, V> extends ReferenceEntry<K, V> {
|
||||||
|
final AbstractReferenceMap<K, V> parent;
|
||||||
|
final Consumer<V> consumer;
|
||||||
|
|
||||||
|
public AccessibleEntry(final AbstractReferenceMap<K, V> parent, final HashEntry<K, V> next, final int hashCode, final K key, final V value, final Consumer<V> consumer) {
|
||||||
|
super(parent, next, hashCode, key, value);
|
||||||
|
this.parent = parent;
|
||||||
|
this.consumer = consumer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPurge() {
|
||||||
|
if (parent.isValueType(ReferenceStrength.HARD)) {
|
||||||
|
consumer.accept(getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue