Add unit test for ZippingIterator, add factory methods to IteratorUtils, add changelog entry.

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/collections/trunk@1682196 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Thomas Neidhart 2015-05-28 09:58:09 +00:00
parent c193f6556f
commit b1e21c8a6e
4 changed files with 292 additions and 34 deletions

View File

@ -27,6 +27,9 @@
Iterable instances. Additionally various supporting methods have been
added to "IterableUtils" and "IteratorUtils".
</action>
<action issue="COLLECTIONS-464" dev="tn" type="add">
Added new "ZippingIterator" and factory methods "IteratorUtils#zippingIterator(...)".
</action>
<action issue="COLLECTIONS-464" dev="tn" type="add">
Added new decorator "SkippingIterator" and factory methods "IteratorUtils#skippingIterator(...)".
</action>

View File

@ -61,6 +61,7 @@ import org.apache.commons.collections4.iterators.TransformIterator;
import org.apache.commons.collections4.iterators.UnmodifiableIterator;
import org.apache.commons.collections4.iterators.UnmodifiableListIterator;
import org.apache.commons.collections4.iterators.UnmodifiableMapIterator;
import org.apache.commons.collections4.iterators.ZippingIterator;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
@ -489,22 +490,6 @@ public class IteratorUtils {
return new BoundedIterator<E>(iterator, offset, max);
}
// Skipping
//-----------------------------------------------------------------------
/**
* Decorates the specified iterator to skip the first N elements.
*
* @param <E> the element type
* @param iterator the iterator to decorate
* @param offset the first number of elements to skip
* @return a new skipping iterator
* @throws IllegalArgumentException if the iterator is null or offset is negative
* @since 4.1
*/
public static <E> SkippingIterator<E> skippingIterator(final Iterator<E> iterator, long offset) {
return new SkippingIterator<E>(iterator, offset);
}
// Unmodifiable
//-----------------------------------------------------------------------
/**
@ -914,6 +899,68 @@ public class IteratorUtils {
return PushbackIterator.pushbackIterator(iterator);
}
// Skipping
//-----------------------------------------------------------------------
/**
* Decorates the specified iterator to skip the first N elements.
*
* @param <E> the element type
* @param iterator the iterator to decorate
* @param offset the first number of elements to skip
* @return a new skipping iterator
* @throws IllegalArgumentException if the iterator is null or offset is negative
* @since 4.1
*/
public static <E> SkippingIterator<E> skippingIterator(final Iterator<E> iterator, long offset) {
return new SkippingIterator<E>(iterator, offset);
}
// Zipping
//-----------------------------------------------------------------------
/**
* Returns an iterator that interleaves elements from the decorated iterators.
*
* @param <E> the element type
* @param a the first iterator to interleave
* @param b the second iterator to interleave
* @return an iterator, interleaving the decorated iterators
* @throws IllegalArgumentException if any iterator is null
* @since 4.1
*/
public static <E> ZippingIterator<E> zippingIterator(final Iterator<? extends E> a, final Iterator<? extends E> b) {
return new ZippingIterator<E>(a, b);
}
/**
* Returns an iterator that interleaves elements from the decorated iterators.
*
* @param <E> the element type
* @param a the first iterator to interleave
* @param b the second iterator to interleave
* @param c the third iterator to interleave
* @return an iterator, interleaving the decorated iterators
* @throws IllegalArgumentException if any iterator is null
* @since 4.1
*/
public static <E> ZippingIterator<E> zippingIterator(final Iterator<? extends E> a,
final Iterator<? extends E> b,
final Iterator<? extends E> c) {
return new ZippingIterator<E>(a, b, c);
}
/**
* Returns an iterator that interleaves elements from the decorated iterators.
*
* @param <E> the element type
* @param iterators the array of iterators to interleave
* @return an iterator, interleaving the decorated iterators
* @throws IllegalArgumentException if any iterator is null
* @since 4.1
*/
public static <E> ZippingIterator<E> zippingIterator(final Iterator<? extends E>... iterators) {
return new ZippingIterator<E>(iterators);
}
// Views
//-----------------------------------------------------------------------
/**

View File

@ -27,9 +27,9 @@ import org.apache.commons.collections4.FluentIterable;
* Provides an interleaved iteration over the elements contained in a
* collection of Iterators.
* <p>
* Given two {@link Iterator} instances <code>A</code> and
* <code>B</code>, the {@link #next} method on this iterator will
* alternate between <code>A.next()</code> and <code>B.next()</code>.
* Given two {@link Iterator} instances {@code A} and {@code B}, the
* {@link #next} method on this iterator will switch between {@code A.next()}
* and {@code B.next()} until both iterators are exhausted.
*
* @since 4.1
* @version $Id$
@ -38,8 +38,10 @@ public class ZippingIterator<E> implements Iterator<E> {
/** The {@link Iterator}s to evaluate. */
private final Iterator<Iterator<? extends E>> iterators;
/** The next iterator to use for next(). */
private Iterator<? extends E> nextIterator = null;
/** The last iterator which was used for next(). */
private Iterator<? extends E> lastReturned = null;
@ -50,8 +52,8 @@ public class ZippingIterator<E> implements Iterator<E> {
* Constructs a new <code>ZippingIterator</code> that will provide
* interleaved iteration over the two given iterators.
*
* @param a the first child iterator
* @param b the second child iterator
* @param a the first child iterator
* @param b the second child iterator
* @throws NullPointerException if either iterator is null
*/
@SuppressWarnings("unchecked")
@ -60,19 +62,34 @@ public class ZippingIterator<E> implements Iterator<E> {
}
/**
* Constructs a new <code>ZippingIterator</code> that will use the
* specified comparator to provide ordered iteration over the array of
* iterators.
* Constructs a new <code>ZippingIterator</code> that will provide
* interleaved iteration over the three given iterators.
*
* @param iterators the array of iterators
* @throws NullPointerException if iterators array is or contains null
* @param a the first child iterator
* @param b the second child iterator
* @param c the third child iterator
* @throws NullPointerException if either iterator is null
*/
@SuppressWarnings("unchecked")
public ZippingIterator(final Iterator<? extends E> a,
final Iterator<? extends E> b,
final Iterator<? extends E> c) {
this(new Iterator[] {a, b, c});
}
/**
* Constructs a new <code>ZippingIterator</code> that will provide
* interleaved iteration of the specified iterators.
*
* @param iterators the array of iterators
* @throws NullPointerException if any iterator is null
*/
public ZippingIterator(final Iterator<? extends E>... iterators) {
// create a mutable list
// create a mutable list to be able to remove exhausted iterators
final List<Iterator<? extends E>> list = new ArrayList<Iterator<? extends E>>();
for (Iterator<? extends E> iterator : iterators) {
for (final Iterator<? extends E> iterator : iterators) {
if (iterator == null) {
throw new NullPointerException("Iterator must not be null");
throw new NullPointerException("Iterator must not be null.");
}
list.add(iterator);
}
@ -83,21 +100,21 @@ public class ZippingIterator<E> implements Iterator<E> {
// -------------------------------------------------------------------
/**
* Returns <code>true</code> if any child iterator has remaining elements.
* Returns {@code true} if any child iterator has remaining elements.
*
* @return true if this iterator has remaining elements
*/
public boolean hasNext() {
// the next iterator has already been determined
// this might happen if hasNext() was called multiple
// this might happen if hasNext() is called multiple
if (nextIterator != null) {
return true;
}
while(iterators.hasNext()) {
final Iterator<? extends E> iterator = iterators.next();
if (iterator.hasNext()) {
nextIterator = iterator;
final Iterator<? extends E> childIterator = iterators.next();
if (childIterator.hasNext()) {
nextIterator = childIterator;
return true;
} else {
// iterator is exhausted, remove it

View File

@ -0,0 +1,191 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.collections4.iterators;
import java.util.ArrayList;
import org.apache.commons.collections4.IteratorUtils;
/**
* Unit test suite for {@link ZippingIterator}.
*
* @version $Id$
*/
@SuppressWarnings("boxing")
public class ZippingIteratorTest extends AbstractIteratorTest<Integer> {
//------------------------------------------------------------ Conventional
public ZippingIteratorTest(final String testName) {
super(testName);
}
//--------------------------------------------------------------- Lifecycle
private ArrayList<Integer> evens = null;
private ArrayList<Integer> odds = null;
private ArrayList<Integer> fib = null;
@Override
public void setUp() throws Exception {
super.setUp();
evens = new ArrayList<Integer>();
odds = new ArrayList<Integer>();
for (int i = 0; i < 20; i++) {
if (0 == i % 2) {
evens.add(i);
} else {
odds.add(i);
}
}
fib = new ArrayList<Integer>();
fib.add(1);
fib.add(1);
fib.add(2);
fib.add(3);
fib.add(5);
fib.add(8);
fib.add(13);
fib.add(21);
}
//---------------------------------------------------- TestIterator Methods
@Override
@SuppressWarnings("unchecked")
public ZippingIterator<Integer> makeEmptyIterator() {
return new ZippingIterator<Integer>(IteratorUtils.<Integer>emptyIterator());
}
@Override
public ZippingIterator<Integer> makeObject() {
return new ZippingIterator<Integer>(evens.iterator(), odds.iterator(), fib.iterator());
}
//------------------------------------------------------------------- Tests
public void testIterateEven() {
@SuppressWarnings("unchecked")
final ZippingIterator<Integer> iter = new ZippingIterator<Integer>(evens.iterator());
for (int i = 0; i < evens.size(); i++) {
assertTrue(iter.hasNext());
assertEquals(evens.get(i), iter.next());
}
assertTrue(!iter.hasNext());
}
public void testIterateEvenOdd() {
final ZippingIterator<Integer> iter = new ZippingIterator<Integer>(evens.iterator(), odds.iterator());
for (int i = 0; i < 20; i++) {
assertTrue(iter.hasNext());
assertEquals(Integer.valueOf(i), iter.next());
}
assertTrue(!iter.hasNext());
}
public void testIterateOddEven() {
final ZippingIterator<Integer> iter = new ZippingIterator<Integer>(odds.iterator(), evens.iterator());
for (int i = 0, j = 0; i < 20; i++) {
assertTrue(iter.hasNext());
int val = iter.next();
if (i % 2 == 0) {
assertEquals(odds.get(j).intValue(), val);
} else {
assertEquals(evens.get(j).intValue(), val);
j++;
}
}
assertTrue(!iter.hasNext());
}
public void testIterateEvenEven() {
final ZippingIterator<Integer> iter = new ZippingIterator<Integer>(evens.iterator(), evens.iterator());
for (int i = 0; i < evens.size(); i++) {
assertTrue(iter.hasNext());
assertEquals(evens.get(i), iter.next());
assertTrue(iter.hasNext());
assertEquals(evens.get(i), iter.next());
}
assertTrue(!iter.hasNext());
}
public void testIterateFibEvenOdd() {
final ZippingIterator<Integer> iter = new ZippingIterator<Integer>(fib.iterator(), evens.iterator(), odds.iterator());
assertEquals(Integer.valueOf(1),iter.next()); // fib 1
assertEquals(Integer.valueOf(0),iter.next()); // even 0
assertEquals(Integer.valueOf(1),iter.next()); // odd 1
assertEquals(Integer.valueOf(1),iter.next()); // fib 1
assertEquals(Integer.valueOf(2),iter.next()); // even 2
assertEquals(Integer.valueOf(3),iter.next()); // odd 3
assertEquals(Integer.valueOf(2),iter.next()); // fib 2
assertEquals(Integer.valueOf(4),iter.next()); // even 4
assertEquals(Integer.valueOf(5),iter.next()); // odd 5
assertEquals(Integer.valueOf(3),iter.next()); // fib 3
assertEquals(Integer.valueOf(6),iter.next()); // even 6
assertEquals(Integer.valueOf(7),iter.next()); // odd 7
assertEquals(Integer.valueOf(5),iter.next()); // fib 5
assertEquals(Integer.valueOf(8),iter.next()); // even 8
assertEquals(Integer.valueOf(9),iter.next()); // odd 9
assertEquals(Integer.valueOf(8),iter.next()); // fib 8
assertEquals(Integer.valueOf(10),iter.next()); // even 10
assertEquals(Integer.valueOf(11),iter.next()); // odd 11
assertEquals(Integer.valueOf(13),iter.next()); // fib 13
assertEquals(Integer.valueOf(12),iter.next()); // even 12
assertEquals(Integer.valueOf(13),iter.next()); // odd 13
assertEquals(Integer.valueOf(21),iter.next()); // fib 21
assertEquals(Integer.valueOf(14),iter.next()); // even 14
assertEquals(Integer.valueOf(15),iter.next()); // odd 15
assertEquals(Integer.valueOf(16),iter.next()); // even 16
assertEquals(Integer.valueOf(17),iter.next()); // odd 17
assertEquals(Integer.valueOf(18),iter.next()); // even 18
assertEquals(Integer.valueOf(19),iter.next()); // odd 19
assertTrue(!iter.hasNext());
}
public void testRemoveFromSingle() {
@SuppressWarnings("unchecked")
final ZippingIterator<Integer> iter = new ZippingIterator<Integer>(evens.iterator());
int expectedSize = evens.size();
while (iter.hasNext()) {
final Object o = iter.next();
final Integer val = (Integer) o;
if (val.intValue() % 4 == 0) {
expectedSize--;
iter.remove();
}
}
assertEquals(expectedSize, evens.size());
}
public void testRemoveFromDouble() {
final ZippingIterator<Integer> iter = new ZippingIterator<Integer>(evens.iterator(), odds.iterator());
int expectedSize = evens.size() + odds.size();
while (iter.hasNext()) {
final Object o = iter.next();
final Integer val = (Integer) o;
if (val.intValue() % 4 == 0 || val.intValue() % 3 == 0) {
expectedSize--;
iter.remove();
}
}
assertEquals(expectedSize, evens.size() + odds.size());
}
}