Fix FastArrayList iterator to work in thread-safe environments

git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/collections/trunk@131802 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Stephen Colebourne 2004-06-23 21:41:49 +00:00
parent 72ed906100
commit ac06284e5a
2 changed files with 96 additions and 25 deletions

View File

@ -57,9 +57,10 @@ import java.util.ListIterator;
* Double-Checked Locking Idiom Is Broken Declaration</a>.</p> * Double-Checked Locking Idiom Is Broken Declaration</a>.</p>
* *
* @since Commons Collections 1.0 * @since Commons Collections 1.0
* @version $Revision: 1.15 $ $Date: 2004/02/18 01:15:42 $ * @version $Revision: 1.16 $ $Date: 2004/06/23 21:41:49 $
* *
* @author Craig R. McClanahan * @author Craig R. McClanahan
* @author Stephen Colebourne
*/ */
public class FastArrayList extends ArrayList { public class FastArrayList extends ArrayList {
@ -486,12 +487,20 @@ public class FastArrayList extends ArrayList {
/** /**
* Return an iterator over the elements in this list in proper sequence. * Return an iterator over the elements in this list in proper sequence.
* <br><br> * <p>
* <strong>IMPLEMENTATION NOTE</strong> - If the list is operating in fast * <b>Thread safety</b><br />
* mode, an Iterator is returned, and a structural modification to the * The iterator returned is thread-safe ONLY in FAST mode.
* list is made, then the Iterator will continue over the previous contents * In slow mode there is no way to synchronize, or make the iterator thread-safe.
* of the list (at the time that the Iterator was created), rather than * <p>
* failing due to concurrent modifications. * In fast mode iteration and modification may occur in parallel on different threads,
* however there is a restriction. Modification must be EITHER via the Iterator
* interface methods OR the List interface. If a mixture of modification
* methods is used a ConcurrentModificationException is thrown from the iterator
* modification method. If the List modification methods are used the changes are
* NOT visible in the iterator (it shows the list contents at the time the iterator
* was created).
*
* @return the iterator
*/ */
public Iterator iterator() { public Iterator iterator() {
if (fast) { if (fast) {
@ -524,7 +533,20 @@ public class FastArrayList extends ArrayList {
/** /**
* Return an iterator of the elements of this list, in proper sequence. * Return an iterator of the elements of this list, in proper sequence.
* See the implementation note on <code>iterator()</code>. * <p>
* <b>Thread safety</b><br />
* The iterator returned is thread-safe ONLY in FAST mode.
* In slow mode there is no way to synchronize, or make the iterator thread-safe.
* <p>
* In fast mode iteration and modification may occur in parallel on different threads,
* however there is a restriction. Modification must be EITHER via the Iterator
* interface methods OR the List interface. If a mixture of modification
* methods is used a ConcurrentModificationException is thrown from the iterator
* modification method. If the List modification methods are used the changes are
* NOT visible in the iterator (it shows the list contents at the time the iterator
* was created).
*
* @return the list iterator
*/ */
public ListIterator listIterator() { public ListIterator listIterator() {
if (fast) { if (fast) {
@ -538,10 +560,21 @@ public class FastArrayList extends ArrayList {
/** /**
* Return an iterator of the elements of this list, in proper sequence, * Return an iterator of the elements of this list, in proper sequence,
* starting at the specified position. * starting at the specified position.
* See the implementation note on <code>iterator()</code>. * <p>
* <b>Thread safety</b><br />
* The iterator returned is thread-safe ONLY in FAST mode.
* In slow mode there is no way to synchronize, or make the iterator thread-safe.
* <p>
* In fast mode iteration and modification may occur in parallel on different threads,
* however there is a restriction. Modification must be EITHER via the Iterator
* interface methods OR the List interface. If a mixture of modification
* methods is used a ConcurrentModificationException is thrown from the iterator
* modification method. If the List modification methods are used the changes are
* NOT visible in the iterator (it shows the list contents at the time the iterator
* was created).
* *
* @param index The starting position of the iterator to return * @param index The starting position of the iterator to return
* * @return the list iterator
* @exception IndexOutOfBoundsException if the index is out of range * @exception IndexOutOfBoundsException if the index is out of range
*/ */
public ListIterator listIterator(int index) { public ListIterator listIterator(int index) {
@ -1205,8 +1238,9 @@ public class FastArrayList extends ArrayList {
int i = nextIndex(); int i = nextIndex();
get().add(i, o); get().add(i, o);
last++; last++;
expected = list;
iter = get().listIterator(i + 1); iter = get().listIterator(i + 1);
lastReturnedIndex = 1; lastReturnedIndex = -1;
} }
} }
@ -1239,34 +1273,28 @@ public class FastArrayList extends ArrayList {
} }
public boolean hasNext() { public boolean hasNext() {
checkMod();
return iter.hasNext(); return iter.hasNext();
} }
public Object next() { public Object next() {
checkMod();
lastReturnedIndex = iter.nextIndex(); lastReturnedIndex = iter.nextIndex();
return iter.next(); return iter.next();
} }
public boolean hasPrevious() { public boolean hasPrevious() {
checkMod();
return iter.hasPrevious(); return iter.hasPrevious();
} }
public Object previous() { public Object previous() {
checkMod();
lastReturnedIndex = iter.previousIndex(); lastReturnedIndex = iter.previousIndex();
return iter.previous(); return iter.previous();
} }
public int previousIndex() { public int previousIndex() {
checkMod();
return iter.previousIndex(); return iter.previousIndex();
} }
public int nextIndex() { public int nextIndex() {
checkMod();
return iter.nextIndex(); return iter.nextIndex();
} }
@ -1295,6 +1323,7 @@ public class FastArrayList extends ArrayList {
checkMod(); checkMod();
int i = nextIndex(); int i = nextIndex();
get().add(i, o); get().add(i, o);
expected = list;
iter = get().listIterator(i + 1); iter = get().listIterator(i + 1);
lastReturnedIndex = -1; lastReturnedIndex = -1;
} }

View File

@ -16,14 +16,17 @@
package org.apache.commons.collections; package org.apache.commons.collections;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.ListIterator;
import junit.framework.Test; import junit.framework.Test;
/** /**
* Test FastArrayList implementation in <strong>fast</strong> mode. * Test FastArrayList implementation in <strong>fast</strong> mode.
* *
* @version $Revision: 1.9 $ $Date: 2004/02/18 01:20:35 $ * @version $Revision: 1.10 $ $Date: 2004/06/23 21:41:49 $
* *
* @author Jason van Zyl * @author Jason van Zyl
*/ */
@ -52,12 +55,51 @@ public class TestFastArrayList1 extends TestFastArrayList {
return (fal); return (fal);
} }
public String[] ignoredTests() { public void testIterateModify1() {
// subList impl result in... List list = makeEmptyList();
return new String[] { list.add("A");
"TestFastArrayList1.bulkTestSubList.bulkTestListIterator.testAddThenSet", list.add("B");
"TestFastArrayList1.bulkTestSubList.bulkTestListIterator.testAddThenRemove", list.add("C");
}; assertEquals(3, list.size());
Iterator it = list.iterator();
assertEquals("A", it.next());
assertEquals(3, list.size());
list.add(1, "Z");
assertEquals(4, list.size());
assertEquals("B", it.next());
assertEquals("C", it.next());
assertEquals(false, it.hasNext());
}
public void testIterateModify2() {
List list = makeEmptyList();
list.add("A");
list.add("B");
list.add("C");
assertEquals(3, list.size());
ListIterator it = list.listIterator();
assertEquals("A", it.next());
it.add("M"); // change via Iterator interface
assertEquals(4, list.size());
list.add(2, "Z"); // change via List interface
assertEquals(5, list.size());
assertEquals("B", it.next());
try {
it.set("N"); // fails as previously changed via List interface
fail();
} catch (ConcurrentModificationException ex) {}
try {
it.remove();
fail();
} catch (ConcurrentModificationException ex) {}
try {
it.add("N");
fail();
} catch (ConcurrentModificationException ex) {}
assertEquals("C", it.next());
assertEquals(false, it.hasNext());
} }
} }