Add ReverseListIterator

rfe 39224, including code from Serge Knystautas

git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/collections/trunk@400329 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Stephen Colebourne 2006-05-06 16:10:31 +00:00
parent 2b0a51fd95
commit 324ef991a6
4 changed files with 346 additions and 0 deletions

View File

@ -58,6 +58,7 @@ If this causes major headaches to anyone please contact commons-dev at jakarta.a
<li>DefaultedMap - Returns a default value when the key is not found, without adding the default value to the map itself [30911]</li>
<li>GrowthList - Decorator that causes set and indexed add to expand the list rather than throw IndexOutOfBoundsException [34171]</li>
<li>LoopingListIterator - When the end of the list is reached the iteration continues from the start [30166]</li>
<li>ReverseListIterator - A list iterator that returns the elements from the list in reverse order [39224]</li>
<li>BoundedBuffer - A new wrapper class which can make any buffer bounded [37473]</li>
</ul>

View File

@ -0,0 +1,174 @@
/*
* Copyright 2006 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.collections.iterators;
import java.util.List;
import java.util.ListIterator;
import org.apache.commons.collections.ResettableListIterator;
/**
* Iterates backwards through a List, starting with the last element
* and continuing to the first. This is useful for looping around
* a list in reverse order without needing to actually reverse the list.
* <p>
* The first call to <code>next()</code> will return the last element
* from the list, and so on. The <code>hasNext()</code> method works
* in concert with the <code>next()</code> method as expected.
* However, the <code>nextIndex()</code> method returns the correct
* index in the list, thus it starts high and reduces as the iteration
* continues. The previous methods work similarly.
*
* @author Serge Knystautas
* @author Stephen Colebourne
* @version $Revision: $ $Date$
*/
public class ReverseListIterator implements ResettableListIterator {
/** The list being wrapped. */
private final List list;
/** The list iterator being wrapped. */
private ListIterator iterator;
/** Flag to indicate if updating is possible at the moment. */
private boolean validForUpdate = true;
/**
* Constructor that wraps a list.
*
* @param list the list to create a reversed iterator for
* @throws NullPointerException if the list is null
*/
public ReverseListIterator(List list) {
super();
this.list = list;
iterator = list.listIterator(list.size());
}
//-----------------------------------------------------------------------
/**
* Checks whether there is another element.
*
* @return true if there is another element
*/
public boolean hasNext() {
return iterator.hasPrevious();
}
/**
* Gets the next element.
* The next element is the previous in the list.
*
* @return the next element in the iterator
*/
public Object next() {
Object obj = iterator.previous();
validForUpdate = true;
return obj;
}
/**
* Gets the index of the next element.
*
* @return the index of the next element in the iterator
*/
public int nextIndex() {
return iterator.previousIndex();
}
/**
* Checks whether there is a previous element.
*
* @return true if there is a previous element
*/
public boolean hasPrevious() {
return iterator.hasNext();
}
/**
* Gets the previous element.
* The next element is the previous in the list.
*
* @return the previous element in the iterator
*/
public Object previous() {
Object obj = iterator.next();
validForUpdate = true;
return obj;
}
/**
* Gets the index of the previous element.
*
* @return the index of the previous element in the iterator
*/
public int previousIndex() {
return iterator.nextIndex();
}
/**
* Removes the last returned element.
*
* @throws UnsupportedOperationException if the list is unmodifiable
* @throws IllegalStateException if there is no element to remove
*/
public void remove() {
if (validForUpdate == false) {
throw new IllegalStateException("Cannot remove from list until next() or previous() called");
}
iterator.remove();
}
/**
* Replaces the last returned element.
*
* @param obj the object to set
* @throws UnsupportedOperationException if the list is unmodifiable
* @throws IllegalStateException if the iterator is not in a valid state for set
*/
public void set(Object obj) {
if (validForUpdate == false) {
throw new IllegalStateException("Cannot set to list until next() or previous() called");
}
iterator.set(obj);
}
/**
* Adds a new element to the list between the next and previous elements.
*
* @param obj the object to add
* @throws UnsupportedOperationException if the list is unmodifiable
* @throws IllegalStateException if the iterator is not in a valid state for set
*/
public void add(Object obj) {
// the validForUpdate flag is needed as the necessary previous()
// method call re-enables remove and add
if (validForUpdate == false) {
throw new IllegalStateException("Cannot add to list until next() or previous() called");
}
validForUpdate = false;
iterator.add(obj);
iterator.previous();
}
/**
* Resets the iterator back to the start (which is the
* end of the list as this is a reversed iterator)
*/
public void reset() {
iterator = list.listIterator(list.size());
}
}

View File

@ -48,6 +48,7 @@ public class TestAll extends TestCase {
suite.addTest(TestListIteratorWrapper.suite());
suite.addTest(TestLoopingIterator.suite());
suite.addTest(TestLoopingListIterator.suite());
suite.addTest(TestReverseListIterator.suite());
suite.addTest(TestSingletonIterator.suite());
suite.addTest(TestSingletonIterator2.suite());
suite.addTest(TestSingletonListIterator.suite());

View File

@ -0,0 +1,170 @@
/*
* Copyright 2006 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.collections.iterators;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;
import junit.framework.Test;
import junit.framework.TestSuite;
import junit.textui.TestRunner;
import org.apache.commons.collections.ResettableListIterator;
/**
* Tests the ReverseListIterator.
*
* @version $Revision: $ $Date$
*/
public class TestReverseListIterator extends AbstractTestListIterator {
protected String[] testArray = { "One", "Two", "Three", "Four" };
public static void main(String args[]) {
String[] testCaseName = { TestReverseListIterator.class.getName() };
TestRunner.main(testCaseName);
}
public static Test suite() {
return new TestSuite(TestReverseListIterator.class);
}
public TestReverseListIterator(String testName) {
super(testName);
}
public ListIterator makeEmptyListIterator() {
List list = new ArrayList();
return new ReverseListIterator(list);
}
public ListIterator makeFullListIterator() {
List list = new ArrayList(Arrays.asList(testArray));
return new ReverseListIterator(list);
}
// overrides
//-----------------------------------------------------------------------
public void testEmptyListIteratorIsIndeedEmpty() {
ListIterator it = makeEmptyListIterator();
assertEquals(false, it.hasNext());
assertEquals(-1, it.nextIndex()); // reversed index
assertEquals(false, it.hasPrevious());
assertEquals(0, it.previousIndex()); // reversed index
// next() should throw a NoSuchElementException
try {
it.next();
fail("NoSuchElementException must be thrown from empty ListIterator");
} catch (NoSuchElementException e) {
}
// previous() should throw a NoSuchElementException
try {
it.previous();
fail("NoSuchElementException must be thrown from empty ListIterator");
} catch (NoSuchElementException e) {
}
}
public void testWalkForwardAndBack() {
ArrayList list = new ArrayList();
ListIterator it = makeFullListIterator();
while (it.hasNext()) {
list.add(it.next());
}
// check state at end
assertEquals(false, it.hasNext());
assertEquals(true, it.hasPrevious());
// this had to be commented out, as there is a bug in the JDK before JDK1.5
// where calling previous at the start of an iterator would push the cursor
// back to an invalid negative value
// try {
// it.next();
// fail("NoSuchElementException must be thrown from next at end of ListIterator");
// } catch (NoSuchElementException e) {
// }
// loop back through comparing
for (int i = list.size() - 1; i >= 0; i--) {
assertEquals("" + i, list.size() - i - 2, it.nextIndex()); // reversed index
assertEquals(list.size() - i - 1, it.previousIndex()); // reversed index
Object obj = list.get(i);
assertEquals(obj, it.previous());
}
// check state at start
assertEquals(true, it.hasNext());
assertEquals(false, it.hasPrevious());
try {
it.previous();
fail("NoSuchElementException must be thrown from previous at start of ListIterator");
} catch (NoSuchElementException e) {
}
}
//-----------------------------------------------------------------------
public void testReverse() {
ListIterator it = makeFullListIterator();
assertEquals(true, it.hasNext());
assertEquals(3, it.nextIndex());
assertEquals(false, it.hasPrevious());
assertEquals(4, it.previousIndex());
assertEquals("Four", it.next());
assertEquals(2, it.nextIndex());
assertEquals(true, it.hasNext());
assertEquals(3, it.previousIndex());
assertEquals(true, it.hasPrevious());
assertEquals("Three", it.next());
assertEquals(true, it.hasNext());
assertEquals(1, it.nextIndex());
assertEquals(true, it.hasPrevious());
assertEquals(2, it.previousIndex());
assertEquals("Two", it.next());
assertEquals(true, it.hasNext());
assertEquals(0, it.nextIndex());
assertEquals(true, it.hasPrevious());
assertEquals(1, it.previousIndex());
assertEquals("One", it.next());
assertEquals(false, it.hasNext());
assertEquals(-1, it.nextIndex());
assertEquals(true, it.hasPrevious());
assertEquals(0, it.previousIndex());
assertEquals("One", it.previous());
assertEquals("Two", it.previous());
assertEquals("Three", it.previous());
assertEquals("Four", it.previous());
}
public void testReset() {
ResettableListIterator it = (ResettableListIterator) makeFullListIterator();
assertEquals("Four", it.next());
it.reset();
assertEquals("Four", it.next());
it.next();
it.next();
it.reset();
assertEquals("Four", it.next());
}
}