Ensure that the keySet and values views handle removed elements
bug 25551, from Joe Raysa git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/collections/trunk@131476 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
f2d99d55bc
commit
a00293d508
|
@ -197,6 +197,9 @@
|
|||
<contributor>
|
||||
<name>Howard Lewis Ship</name>
|
||||
</contributor>
|
||||
<contributor>
|
||||
<name>Joe Raysa</name>
|
||||
</contributor>
|
||||
<contributor>
|
||||
<name>Jan Sorensen</name>
|
||||
</contributor>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/java/org/apache/commons/collections/map/ReferenceMap.java,v 1.4 2003/12/28 16:36:48 scolebourne Exp $
|
||||
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/java/org/apache/commons/collections/map/ReferenceMap.java,v 1.5 2003/12/29 14:54:58 scolebourne Exp $
|
||||
* ====================================================================
|
||||
*
|
||||
* The Apache Software License, Version 1.1
|
||||
|
@ -119,7 +119,7 @@ import org.apache.commons.collections.keyvalue.DefaultMapEntry;
|
|||
* @see java.lang.ref.Reference
|
||||
*
|
||||
* @since Commons Collections 3.0 (previously in main package v2.1)
|
||||
* @version $Revision: 1.4 $ $Date: 2003/12/28 16:36:48 $
|
||||
* @version $Revision: 1.5 $ $Date: 2003/12/29 14:54:58 $
|
||||
*
|
||||
* @author Paul Jack
|
||||
*/
|
||||
|
@ -706,7 +706,7 @@ public class ReferenceMap extends AbstractMap {
|
|||
if (keySet != null) return keySet;
|
||||
keySet = new AbstractSet() {
|
||||
public int size() {
|
||||
return size;
|
||||
return ReferenceMap.this.size();
|
||||
}
|
||||
|
||||
public Iterator iterator() {
|
||||
|
@ -727,6 +727,17 @@ public class ReferenceMap extends AbstractMap {
|
|||
ReferenceMap.this.clear();
|
||||
}
|
||||
|
||||
public Object[] toArray() {
|
||||
return toArray(new Object[0]);
|
||||
}
|
||||
|
||||
public Object[] toArray(Object[] arr) {
|
||||
Collection c = new ArrayList(size());
|
||||
for (Iterator it = iterator(); it.hasNext(); ) {
|
||||
c.add(it.next());
|
||||
}
|
||||
return c.toArray(arr);
|
||||
}
|
||||
};
|
||||
return keySet;
|
||||
}
|
||||
|
@ -741,7 +752,7 @@ public class ReferenceMap extends AbstractMap {
|
|||
if (values != null) return values;
|
||||
values = new AbstractCollection() {
|
||||
public int size() {
|
||||
return size;
|
||||
return ReferenceMap.this.size();
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
|
@ -751,13 +762,29 @@ public class ReferenceMap extends AbstractMap {
|
|||
public Iterator iterator() {
|
||||
return new ValueIterator();
|
||||
}
|
||||
|
||||
public Object[] toArray() {
|
||||
return toArray(new Object[0]);
|
||||
}
|
||||
|
||||
public Object[] toArray(Object[] arr) {
|
||||
Collection c = new ArrayList(size());
|
||||
for (Iterator it = iterator(); it.hasNext(); ) {
|
||||
c.add(it.next());
|
||||
}
|
||||
return c.toArray(arr);
|
||||
}
|
||||
};
|
||||
return values;
|
||||
}
|
||||
|
||||
|
||||
// If getKey() or getValue() returns null, it means
|
||||
// the mapping is stale and should be removed.
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* A MapEntry implementation for the map.
|
||||
* <p>
|
||||
* If getKey() or getValue() returns null, it means
|
||||
* the mapping is stale and should be removed.
|
||||
*/
|
||||
private class Entry implements Map.Entry, KeyValue {
|
||||
|
||||
Object key;
|
||||
|
@ -765,7 +792,6 @@ public class ReferenceMap extends AbstractMap {
|
|||
int hash;
|
||||
Entry next;
|
||||
|
||||
|
||||
public Entry(Object key, int hash, Object value, Entry next) {
|
||||
this.key = key;
|
||||
this.hash = hash;
|
||||
|
@ -773,54 +799,56 @@ public class ReferenceMap extends AbstractMap {
|
|||
this.next = next;
|
||||
}
|
||||
|
||||
|
||||
public Object getKey() {
|
||||
return (keyType > HARD) ? ((Reference)key).get() : key;
|
||||
}
|
||||
|
||||
|
||||
public Object getValue() {
|
||||
return (valueType > HARD) ? ((Reference)value).get() : value;
|
||||
}
|
||||
|
||||
|
||||
public Object setValue(Object object) {
|
||||
public Object setValue(Object obj) {
|
||||
Object old = getValue();
|
||||
if (valueType > HARD) ((Reference)value).clear();
|
||||
value = toReference(valueType, object, hash);
|
||||
if (valueType > HARD) {
|
||||
((Reference)value).clear();
|
||||
}
|
||||
value = toReference(valueType, obj, hash);
|
||||
return old;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (obj instanceof Map.Entry == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (o == null) return false;
|
||||
if (o == this) return true;
|
||||
if (!(o instanceof Map.Entry)) return false;
|
||||
|
||||
Map.Entry entry = (Map.Entry)o;
|
||||
Map.Entry entry = (Map.Entry)obj;
|
||||
Object key = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
if ((key == null) || (value == null)) return false;
|
||||
if ((key == null) || (value == null)) {
|
||||
return false;
|
||||
}
|
||||
return key.equals(getKey()) && value.equals(getValue());
|
||||
}
|
||||
|
||||
|
||||
public int hashCode() {
|
||||
Object v = getValue();
|
||||
return hash ^ ((v == null) ? 0 : v.hashCode());
|
||||
}
|
||||
|
||||
|
||||
public String toString() {
|
||||
return getKey() + "=" + getValue();
|
||||
}
|
||||
|
||||
|
||||
boolean purge(Reference ref) {
|
||||
boolean r = (keyType > HARD) && (key == ref);
|
||||
r = r || ((valueType > HARD) && (value == ref));
|
||||
if (r) {
|
||||
if (keyType > HARD) ((Reference)key).clear();
|
||||
if (keyType > HARD) {
|
||||
((Reference)key).clear();
|
||||
}
|
||||
if (valueType > HARD) {
|
||||
((Reference)value).clear();
|
||||
} else if (purgeValues) {
|
||||
|
@ -831,7 +859,9 @@ public class ReferenceMap extends AbstractMap {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The EntrySet iterator.
|
||||
*/
|
||||
private class EntryIterator implements Iterator {
|
||||
// These fields keep track of where we are in the table.
|
||||
int index;
|
||||
|
@ -846,7 +876,6 @@ public class ReferenceMap extends AbstractMap {
|
|||
|
||||
int expectedModCount;
|
||||
|
||||
|
||||
public EntryIterator() {
|
||||
index = (size() != 0 ? table.length : 0);
|
||||
// have to do this here! size() invocation above
|
||||
|
@ -854,7 +883,6 @@ public class ReferenceMap extends AbstractMap {
|
|||
expectedModCount = modCount;
|
||||
}
|
||||
|
||||
|
||||
public boolean hasNext() {
|
||||
checkMod();
|
||||
while (nextNull()) {
|
||||
|
@ -873,26 +901,28 @@ public class ReferenceMap extends AbstractMap {
|
|||
}
|
||||
nextKey = e.getKey();
|
||||
nextValue = e.getValue();
|
||||
if (nextNull()) entry = entry.next;
|
||||
if (nextNull()) {
|
||||
entry = entry.next;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private void checkMod() {
|
||||
if (modCount != expectedModCount) {
|
||||
throw new ConcurrentModificationException();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private boolean nextNull() {
|
||||
return (nextKey == null) || (nextValue == null);
|
||||
}
|
||||
|
||||
protected Entry nextEntry() {
|
||||
checkMod();
|
||||
if (nextNull() && !hasNext()) throw new NoSuchElementException();
|
||||
if (nextNull() && !hasNext()) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
previous = entry;
|
||||
entry = entry.next;
|
||||
currentKey = nextKey;
|
||||
|
@ -902,71 +932,73 @@ public class ReferenceMap extends AbstractMap {
|
|||
return previous;
|
||||
}
|
||||
|
||||
|
||||
public Object next() {
|
||||
return nextEntry();
|
||||
}
|
||||
|
||||
|
||||
public void remove() {
|
||||
checkMod();
|
||||
if (previous == null) throw new IllegalStateException();
|
||||
if (previous == null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
ReferenceMap.this.remove(currentKey);
|
||||
previous = null;
|
||||
currentKey = null;
|
||||
currentValue = null;
|
||||
expectedModCount = modCount;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The values iterator.
|
||||
*/
|
||||
private class ValueIterator extends EntryIterator {
|
||||
public Object next() {
|
||||
return nextEntry().getValue();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The keySet iterator.
|
||||
*/
|
||||
private class KeyIterator extends EntryIterator {
|
||||
public Object next() {
|
||||
return nextEntry().getKey();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
// These two classes store the hashCode of the key of
|
||||
// of the mapping, so that after they're dequeued a quick
|
||||
// lookup of the bucket in the table can occur.
|
||||
|
||||
|
||||
/**
|
||||
* A soft reference holder.
|
||||
*/
|
||||
private static class SoftRef extends SoftReference {
|
||||
private int hash;
|
||||
|
||||
|
||||
public SoftRef(int hash, Object r, ReferenceQueue q) {
|
||||
super(r, q);
|
||||
this.hash = hash;
|
||||
}
|
||||
|
||||
|
||||
public int hashCode() {
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A weak reference holder.
|
||||
*/
|
||||
private static class WeakRef extends WeakReference {
|
||||
private int hash;
|
||||
|
||||
|
||||
public WeakRef(int hash, Object r, ReferenceQueue q) {
|
||||
super(r, q);
|
||||
this.hash = hash;
|
||||
}
|
||||
|
||||
|
||||
public int hashCode() {
|
||||
return hash;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue