[COLLECTIONS-454] Fix findbugs warnings by returning independent Map.Entry objects from an entrySet() iterator in Flat3Map.

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/collections/trunk@1476813 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Thomas Neidhart 2013-04-28 18:52:50 +00:00
parent ce86f3f8aa
commit 9ea429c977
2 changed files with 88 additions and 58 deletions

View File

@ -22,6 +22,11 @@
<body> <body>
<release version="4.0" date="TBA" description="Next release"> <release version="4.0" date="TBA" description="Next release">
<action issue="COLLECTIONS-454" dev="tn" type="update">
An iterator over a "Flat3Map#entrySet()" will now return
independent Map.Entry objects that will not change anymore when
the iterator progresses.
</action>
<action issue="COLLECTIONS-452" dev="tn" type="update"> <action issue="COLLECTIONS-452" dev="tn" type="update">
Change base package to org.apache.commons.collections4. Change base package to org.apache.commons.collections4.
</action> </action>

View File

@ -713,9 +713,10 @@ public class Flat3Map<K, V> implements IterableMap<K, V>, Serializable, Cloneabl
/** /**
* Gets the entrySet view of the map. * Gets the entrySet view of the map.
* Changes made to the view affect this map. * Changes made to the view affect this map.
* The Map Entry is not an independent object and changes as the * <p>
* iterator progresses. * NOTE: from 4.0, the returned Map Entry will be an independent object and will
* To simply iterate through the entries, use {@link #mapIterator()}. * not change anymore as the iterator progresses. To avoid this additional object
* creation and simply iterate through the entries, use {@link #mapIterator()}.
* *
* @return the entrySet view * @return the entrySet view
*/ */
@ -771,45 +772,35 @@ public class Flat3Map<K, V> implements IterableMap<K, V>, Serializable, Cloneabl
} }
} }
static abstract class EntryIterator<K, V> implements Map.Entry<K, V> { static class FlatMapEntry<K, V> implements Map.Entry<K, V> {
private final Flat3Map<K, V> parent; private final Flat3Map<K, V> parent;
private int nextIndex = 0; private final int index;
protected boolean canRemove = false; private volatile boolean removed;
public FlatMapEntry(final Flat3Map<K, V> parent, final int index) {
this.parent = parent;
this.index = index;
this.removed = false;
}
/** /**
* Create a new Flat3Map.EntryIterator. * Used by the iterator that created this entry to indicate that
* {@link java.util.Iterator#remove()} has been called.
* <p>
* As a consequence, all subsequent call to {@link #getKey()},
* {@link #setValue(Object)} and {@link #getValue()} will fail.
*
* @param flag
*/ */
public EntryIterator(final Flat3Map<K, V> parent) { void setRemoved(final boolean flag) {
this.parent = parent; this.removed = flag;
}
public boolean hasNext() {
return nextIndex < parent.size;
}
public Map.Entry<K, V> nextEntry() {
if (hasNext() == false) {
throw new NoSuchElementException(AbstractHashedMap.NO_NEXT_ENTRY);
}
canRemove = true;
nextIndex++;
return this;
}
public void remove() {
if (canRemove == false) {
throw new IllegalStateException(AbstractHashedMap.REMOVE_INVALID);
}
parent.remove(getKey());
nextIndex--;
canRemove = false;
} }
public K getKey() { public K getKey() {
if (canRemove == false) { if (removed) {
throw new IllegalStateException(AbstractHashedMap.GETKEY_INVALID); throw new IllegalStateException(AbstractHashedMap.GETKEY_INVALID);
} }
switch (nextIndex) { switch (index) {
case 3: case 3:
return parent.key3; return parent.key3;
case 2: case 2:
@ -821,10 +812,10 @@ public class Flat3Map<K, V> implements IterableMap<K, V>, Serializable, Cloneabl
} }
public V getValue() { public V getValue() {
if (canRemove == false) { if (removed) {
throw new IllegalStateException(AbstractHashedMap.GETVALUE_INVALID); throw new IllegalStateException(AbstractHashedMap.GETVALUE_INVALID);
} }
switch (nextIndex) { switch (index) {
case 3: case 3:
return parent.value3; return parent.value3;
case 2: case 2:
@ -836,11 +827,11 @@ public class Flat3Map<K, V> implements IterableMap<K, V>, Serializable, Cloneabl
} }
public V setValue(final V value) { public V setValue(final V value) {
if (canRemove == false) { if (removed) {
throw new IllegalStateException(AbstractHashedMap.SETVALUE_INVALID); throw new IllegalStateException(AbstractHashedMap.SETVALUE_INVALID);
} }
final V old = getValue(); final V old = getValue();
switch (nextIndex) { switch (index) {
case 3: case 3:
parent.value3 = value; parent.value3 = value;
break; break;
@ -853,24 +844,10 @@ public class Flat3Map<K, V> implements IterableMap<K, V>, Serializable, Cloneabl
} }
return old; return old;
} }
}
/**
* EntrySetIterator and MapEntry
*/
static class EntrySetIterator<K, V> extends EntryIterator<K, V> implements Iterator<Map.Entry<K, V>> {
EntrySetIterator(final Flat3Map<K, V> parent) {
super(parent);
}
public Map.Entry<K, V> next() {
return nextEntry();
}
@Override @Override
public boolean equals(final Object obj) { public boolean equals(final Object obj) {
if (canRemove == false) { if (removed) {
return false; return false;
} }
if (obj instanceof Map.Entry == false) { if (obj instanceof Map.Entry == false) {
@ -885,7 +862,7 @@ public class Flat3Map<K, V> implements IterableMap<K, V>, Serializable, Cloneabl
@Override @Override
public int hashCode() { public int hashCode() {
if (canRemove == false) { if (removed) {
return 0; return 0;
} }
final Object key = getKey(); final Object key = getKey();
@ -896,11 +873,61 @@ public class Flat3Map<K, V> implements IterableMap<K, V>, Serializable, Cloneabl
@Override @Override
public String toString() { public String toString() {
if (canRemove) { if (!removed) {
return getKey() + "=" + getValue(); return getKey() + "=" + getValue();
} }
return ""; return "";
} }
}
static abstract class EntryIterator<K, V> {
private final Flat3Map<K, V> parent;
private int nextIndex = 0;
protected FlatMapEntry<K, V> currentEntry = null;
/**
* Create a new Flat3Map.EntryIterator.
*/
public EntryIterator(final Flat3Map<K, V> parent) {
this.parent = parent;
}
public boolean hasNext() {
return nextIndex < parent.size;
}
public Map.Entry<K, V> nextEntry() {
if (!hasNext()) {
throw new NoSuchElementException(AbstractHashedMap.NO_NEXT_ENTRY);
}
currentEntry = new FlatMapEntry<K, V>(parent, ++nextIndex);
return currentEntry;
}
public void remove() {
if (currentEntry == null) {
throw new IllegalStateException(AbstractHashedMap.REMOVE_INVALID);
}
currentEntry.setRemoved(true);
parent.remove(currentEntry.getKey());
nextIndex--;
currentEntry = null;
}
}
/**
* EntrySetIterator and MapEntry
*/
static class EntrySetIterator<K, V> extends EntryIterator<K, V> implements Iterator<Map.Entry<K, V>> {
EntrySetIterator(final Flat3Map<K, V> parent) {
super(parent);
}
public Map.Entry<K, V> next() {
return nextEntry();
}
} }
/** /**
@ -973,8 +1000,7 @@ public class Flat3Map<K, V> implements IterableMap<K, V>, Serializable, Cloneabl
} }
public K next() { public K next() {
nextEntry(); return nextEntry().getKey();
return getKey();
} }
} }
@ -1041,8 +1067,7 @@ public class Flat3Map<K, V> implements IterableMap<K, V>, Serializable, Cloneabl
} }
public V next() { public V next() {
nextEntry(); return nextEntry().getValue();
return getValue();
} }
} }