From 14259d07414b465d512dae676f20e1d53e87dd2b Mon Sep 17 00:00:00 2001 From: Anant Date: Mon, 14 Aug 2023 20:18:14 +0800 Subject: [PATCH 1/3] [COLLECTIONS-795] Add a new Iterator to allowing zipping over two iterators of different types This is different from ZippingIterator which only permits sub-iterators of same type. --- pom.xml | 3 + .../commons/collections4/IterableUtils.java | 36 ++++ .../commons/collections4/IteratorUtils.java | 52 ++---- .../commons/collections4/PairedIterable.java | 103 +++++++++++ .../iterators/PairedIterator.java | 171 ++++++++++++++++++ .../collections4/IteratorUtilsTest.java | 18 +- .../collections4/PairedIterableTest.java | 156 ++++++++++++++++ .../iterators/PairedIteratorTest.java | 152 ++++++++++++++++ 8 files changed, 649 insertions(+), 42 deletions(-) create mode 100644 src/main/java/org/apache/commons/collections4/PairedIterable.java create mode 100644 src/main/java/org/apache/commons/collections4/iterators/PairedIterator.java create mode 100644 src/test/java/org/apache/commons/collections4/PairedIterableTest.java create mode 100644 src/test/java/org/apache/commons/collections4/iterators/PairedIteratorTest.java diff --git a/pom.xml b/pom.xml index cb1ddf6a8d..2b842aa844 100644 --- a/pom.xml +++ b/pom.xml @@ -461,6 +461,9 @@ Arturo Bernal + + Anant Damle + diff --git a/src/main/java/org/apache/commons/collections4/IterableUtils.java b/src/main/java/org/apache/commons/collections4/IterableUtils.java index f924f91436..f2b3800be4 100644 --- a/src/main/java/org/apache/commons/collections4/IterableUtils.java +++ b/src/main/java/org/apache/commons/collections4/IterableUtils.java @@ -27,6 +27,7 @@ import org.apache.commons.collections4.functors.EqualPredicate; import org.apache.commons.collections4.iterators.LazyIteratorChain; +import org.apache.commons.collections4.iterators.PairedIterator.PairedItem; import org.apache.commons.collections4.iterators.ReverseListIterator; import org.apache.commons.collections4.iterators.UniqueFilterIterator; @@ -518,6 +519,41 @@ public Iterator iterator() { }; } + /** + * Provides iteration over the elements contained in a pair of Iterables in-tandem. + *

+ * The returned iterable has an iterator that traverses the elements in {@code a} + * and {@code b} together until one of the iterables is traversed completely. + *

+ * + *

+ * The returned iterable's iterator does NOT support {@code remove()}. + *

+ * + * @param the left elements' type + * @param the right elements' type + * @param left the iterable for the left side elements + * @param right the iterable for the right side elements + * @return an iterable, over the decorated iterables to traverse them together until one is + * exhausted + * @throws NullPointerException if any iterator is null + * @since 4.5 + */ + public static Iterable> pairedIterable(final Iterable left, final Iterable right) { + checkNotNull(left); + checkNotNull(right); + + return new FluentIterable>(){ + @Override + public Iterator> iterator() { + return IteratorUtils.pairedIterator(left.iterator(), right.iterator()); + } + }; + } + + // Utility methods + // ---------------------------------------------------------------------- + /** * Returns an immutable empty iterable if the argument is null, * or the argument itself otherwise. diff --git a/src/main/java/org/apache/commons/collections4/IteratorUtils.java b/src/main/java/org/apache/commons/collections4/IteratorUtils.java index 42c9a49f28..e6c2d4b008 100644 --- a/src/main/java/org/apache/commons/collections4/IteratorUtils.java +++ b/src/main/java/org/apache/commons/collections4/IteratorUtils.java @@ -30,38 +30,7 @@ import java.util.Objects; import org.apache.commons.collections4.functors.EqualPredicate; -import org.apache.commons.collections4.iterators.ArrayIterator; -import org.apache.commons.collections4.iterators.ArrayListIterator; -import org.apache.commons.collections4.iterators.BoundedIterator; -import org.apache.commons.collections4.iterators.CollatingIterator; -import org.apache.commons.collections4.iterators.EmptyIterator; -import org.apache.commons.collections4.iterators.EmptyListIterator; -import org.apache.commons.collections4.iterators.EmptyMapIterator; -import org.apache.commons.collections4.iterators.EmptyOrderedIterator; -import org.apache.commons.collections4.iterators.EmptyOrderedMapIterator; -import org.apache.commons.collections4.iterators.EnumerationIterator; -import org.apache.commons.collections4.iterators.FilterIterator; -import org.apache.commons.collections4.iterators.FilterListIterator; -import org.apache.commons.collections4.iterators.IteratorChain; -import org.apache.commons.collections4.iterators.IteratorEnumeration; -import org.apache.commons.collections4.iterators.IteratorIterable; -import org.apache.commons.collections4.iterators.ListIteratorWrapper; -import org.apache.commons.collections4.iterators.LoopingIterator; -import org.apache.commons.collections4.iterators.LoopingListIterator; -import org.apache.commons.collections4.iterators.NodeListIterator; -import org.apache.commons.collections4.iterators.ObjectArrayIterator; -import org.apache.commons.collections4.iterators.ObjectArrayListIterator; -import org.apache.commons.collections4.iterators.ObjectGraphIterator; -import org.apache.commons.collections4.iterators.PeekingIterator; -import org.apache.commons.collections4.iterators.PushbackIterator; -import org.apache.commons.collections4.iterators.SingletonIterator; -import org.apache.commons.collections4.iterators.SingletonListIterator; -import org.apache.commons.collections4.iterators.SkippingIterator; -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.apache.commons.collections4.iterators.*; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -889,6 +858,25 @@ public static ZippingIterator zippingIterator(final Iterator return new ZippingIterator<>(iterators); } + /** + * Gets an Iterator over the elements contained in a pair of Iterables in-tandem. + *

+ * The returned iterator traverses the elements in {@code a} and {@code b} together until one of the iterators + * is exhausted. + *

+ * The returned iterator does NOT support {@code remove()}. + * + * @param the left elements' type + * @param the right elements' type + * @param left the iterator for the left side elements + * @param right the iterator for the right side elements + * @return an iterator, to iterate over the decorated iterators together until one is exhausted + * @throws NullPointerException if any iterator is null + */ + public static PairedIterator pairedIterator(final Iterator left, Iterator right) { + return PairedIterator.of(left, right); + } + // Views /** * Gets an iterator that provides an iterator view of the given enumeration. diff --git a/src/main/java/org/apache/commons/collections4/PairedIterable.java b/src/main/java/org/apache/commons/collections4/PairedIterable.java new file mode 100644 index 0000000000..2d0559558b --- /dev/null +++ b/src/main/java/org/apache/commons/collections4/PairedIterable.java @@ -0,0 +1,103 @@ +/* + * 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; + +import org.apache.commons.collections4.iterators.PairedIterator; +import org.apache.commons.collections4.iterators.PairedIterator.PairedItem; + +import java.util.Iterator; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +import static java.util.Objects.requireNonNull; + +/** + * Provides iteration over the elements contained in a pair of Iterables in-tandem. + * + *

+ * Given two {@link Iterable} instances {@code A} and {@code B}, the {@link #iterator} method on this + * iterator provide a Pair of {@code A.next()} and {@code B.next()} until one of the iterators is + * exhausted. + *

+ * This can simplify zipping over two iterables using the for-each construct. + * Example usage: + *
{@code
+ *   List studentIds = ...
+ *   List studentNames = ...
+ *
+ *   for (PairedItem items : PairedIterable.of(studentIds, studentNames) {
+ *     Integer studentId = item.getLeft();
+ *     String studentName = item.getRight();
+ *     ...
+ *   }
+ * }
+ * + * @param the left elements' type + * @param the right elements' type + */ +public class PairedIterable implements Iterable> { + + /** + * The left {@link Iterable}s to evaluate. + */ + private final Iterable leftIterable; + + /** + * The right {@link Iterable}s to evaluate. + */ + private final Iterable rightIterable; + + // Constructor + // ---------------------------------------------------------------------- + + /** + * Constructs a new {@code PairedIterable} that will provide iteration over two given iterables. + * + * @param leftIterable the iterable for the left side element. + * @param rightIterable the iterable for the right side element. + * @throws NullPointerException if either iterator is null + */ + public PairedIterable(Iterable leftIterable, Iterable rightIterable) { + this.leftIterable = requireNonNull(leftIterable); + this.rightIterable = requireNonNull(rightIterable); + } + + /** + * Convenience static factory to construct the PairedIterable from provided + * {@link Iterable} sources. + * + * @param leftIterable the iterable for the left side element. + * @param rightIterable the iterable for the right side element. + * @return the Iterable to iterate over the elements derived from the provided iterables. + * @throws NullPointerException if either iterables is null + */ + public static PairedIterable of(Iterable leftIterable, Iterable rightIterable) { + return new PairedIterable<>(leftIterable, rightIterable); + } + + // Iterable Methods + // ------------------------------------------------------------------- + + @Override + public Iterator> iterator() { + return PairedIterator.ofIterables(leftIterable, rightIterable); + } + + public Stream> stream() { + return StreamSupport.stream(spliterator(), /*parallel=*/ false); + } +} diff --git a/src/main/java/org/apache/commons/collections4/iterators/PairedIterator.java b/src/main/java/org/apache/commons/collections4/iterators/PairedIterator.java new file mode 100644 index 0000000000..044056058a --- /dev/null +++ b/src/main/java/org/apache/commons/collections4/iterators/PairedIterator.java @@ -0,0 +1,171 @@ +/* + * 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 static java.util.Objects.requireNonNull; + +import java.util.Iterator; +import java.util.NoSuchElementException; +import org.apache.commons.collections4.iterators.PairedIterator.PairedItem; + +/** + * Provides a iteration over the elements contained in a pair of Iterators. + * + *

+ * Given two {@link Iterator} instances {@code A} and {@code B}, the {@link #next} method on this + * iterator provide a Pair of {@code A.next()} and {@code B.next()} until one of the iterators is + * exhausted. + *

+ * Example usage: + *
{@code
+ *   List studentIds = ...
+ *   List studentNames = ...
+ *
+ *   PairedIterator> pairedIterator =
+ *     PairedIterator.ofIterables(studentIds, studentNames);
+ *
+ *   while (pairedIterator.hasNext()) {
+ *     PairedItem item = zippedIterator.next();
+ *     ...
+ *   }
+ * }
+ * + * @param the left elements' type + * @param the right elements' type + */ +public class PairedIterator implements Iterator> { + + /** + * The left {@link Iterator}s to evaluate. + */ + private final Iterator leftIterator; + + /** + * The right {@link Iterator}s to evaluate. + */ + private final Iterator rightIterator; + + // Constructor + // ---------------------------------------------------------------------- + + /** + * Constructs a new {@code ZipPairIterator} that will provide iteration over the two given + * iterators. + * + * @param leftIterator the iterator for the left side element. + * @param rightIterator the iterator for the right side element. + * @throws NullPointerException if either iterator is null + */ + public PairedIterator(Iterator leftIterator, Iterator rightIterator) { + this.leftIterator = requireNonNull(leftIterator); + this.rightIterator = requireNonNull(rightIterator); + } + + /** + * Convenience static factory to construct the ZipPairIterator + * + * @param leftIterator the iterator for the left side element. + * @param rightIterator the iterator for the right side element. + * @return the iterator to iterate over the provided iterators. + * @throws NullPointerException if either iterator is null + */ + public static PairedIterator of(Iterator leftIterator, Iterator rightIterator) { + return new PairedIterator<>(leftIterator, rightIterator); + } + + /** + * Convenience static factory to construct the ZipPairIterator from any {@link Iterable} sources. + * + * @param leftIterable the iterable for the left side element. + * @param rightIterable the iterable for the right side element. + * @return the iterator to iterate over the iterators derived from the provided iterables. + * @throws NullPointerException if either iterables is null + */ + public static PairedIterator ofIterables(Iterable leftIterable, Iterable rightIterable) { + return of(requireNonNull(leftIterable).iterator(), requireNonNull(rightIterable).iterator()); + } + + // Iterator Methods + // ------------------------------------------------------------------- + + /** + * Returns {@code true} if both the child iterators have remaining elements. + * + * @return true if both the child iterators have remaining elements + */ + @Override + public boolean hasNext() { + return leftIterator.hasNext() && rightIterator.hasNext(); + } + + /** + * Returns the next elements from both the child iterators. + * + * @return the next elements from both the iterators. + * @throws NoSuchElementException if any one child iterator is exhausted. + */ + @Override + public PairedItem next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + + return PairedItem.of(leftIterator.next(), rightIterator.next()); + } + + /** + * An immutable tuple class to represent elements from both the iterators. + * + * @param the left elements' type + * @param the right elements' type + */ + public static final class PairedItem { + + private final L leftItem; + + private final R rightItem; + + private PairedItem(L leftItem, R rightItem) { + this.leftItem = leftItem; + this.rightItem = rightItem; + } + + /** + * Convenience static factory method to construct the tuple pair. + * + * @param left the left element + * @param right the right element + * @return the Immutable tuple pair of two elements. + */ + private static PairedItem of(L left, R right) { + return new PairedItem<>(left, right); + } + + public L getLeftItem() { + return leftItem; + } + + public R getRightItem() { + return rightItem; + } + + @Override + public String toString() { + return String.format("{%s, %s}", leftItem, rightItem); + } + } +} diff --git a/src/test/java/org/apache/commons/collections4/IteratorUtilsTest.java b/src/test/java/org/apache/commons/collections4/IteratorUtilsTest.java index 01587f9f55..b653ede93c 100644 --- a/src/test/java/org/apache/commons/collections4/IteratorUtilsTest.java +++ b/src/test/java/org/apache/commons/collections4/IteratorUtilsTest.java @@ -46,16 +46,7 @@ import java.util.Set; import java.util.Vector; -import org.apache.commons.collections4.iterators.ArrayIterator; -import org.apache.commons.collections4.iterators.EmptyIterator; -import org.apache.commons.collections4.iterators.EmptyListIterator; -import org.apache.commons.collections4.iterators.EmptyMapIterator; -import org.apache.commons.collections4.iterators.EmptyOrderedIterator; -import org.apache.commons.collections4.iterators.EmptyOrderedMapIterator; -import org.apache.commons.collections4.iterators.EnumerationIterator; -import org.apache.commons.collections4.iterators.NodeListIterator; -import org.apache.commons.collections4.iterators.ObjectArrayIterator; -import org.apache.commons.collections4.iterators.ZippingIterator; +import org.apache.commons.collections4.iterators.*; import org.apache.commons.collections4.map.EntrySetToMapIteratorAdapter; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -1116,4 +1107,11 @@ public void testZippingIterator() { assertTrue(IteratorUtils.zippingIterator(ie, ie) instanceof ZippingIterator, "create instance fail"); } + @Test + public void testPairedIterator() { + final ArrayList stringList = new ArrayList<>(); + final ArrayList integerList = new ArrayList<>(); + + assertTrue(IteratorUtils.pairedIterator(stringList.iterator(), integerList.iterator()) instanceof PairedIterator, "create instance failed"); + } } diff --git a/src/test/java/org/apache/commons/collections4/PairedIterableTest.java b/src/test/java/org/apache/commons/collections4/PairedIterableTest.java new file mode 100644 index 0000000000..034ebac6a2 --- /dev/null +++ b/src/test/java/org/apache/commons/collections4/PairedIterableTest.java @@ -0,0 +1,156 @@ +/* + * 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; + +import org.apache.commons.collections4.iterators.PairedIterator.PairedItem; +import org.junit.Test; +import org.junit.experimental.runners.Enclosed; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import java.util.*; +import java.util.stream.IntStream; + +import static java.util.Collections.unmodifiableList; +import static java.util.stream.Collectors.toList; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +@RunWith(Enclosed.class) +public final class PairedIterableTest { + + private static final int SMALL_LIST_SIZE = 20; + private static final int LARGE_LIST_SIZE = 40; + + private static final List SMALL_STRINGS_LIST = + unmodifiableList(stringsList(SMALL_LIST_SIZE)); + private static final List LARGE_STRINGS_LIST = + unmodifiableList(stringsList(LARGE_LIST_SIZE)); + private static final List SMALL_INTS_LIST = unmodifiableList(intsList(SMALL_LIST_SIZE)); + private static final List LARGE_INTS_LIST = unmodifiableList(intsList(LARGE_LIST_SIZE)); + + @RunWith(Parameterized.class) + public static final class ParameterizedTests { + private final String testCondition; + private final List leftList; + private final List rightList; + private final int expectedIterableSize; + + public ParameterizedTests( + String testCondition, List leftList, List rightList, int expectedIterableSize) { + this.testCondition = testCondition; + this.leftList = leftList; + this.rightList = rightList; + this.expectedIterableSize = expectedIterableSize; + } + + @Test + public void testAllowsForEach() { + ArrayList> outputPairedIterable = new ArrayList<>(); + for (PairedItem item : PairedIterable.of(leftList, rightList)) { + outputPairedIterable.add(item); + } + + assertEquals(expectedIterableSize, outputPairedIterable.size()); + + for (int i = 0; i < outputPairedIterable.size(); i++) { + PairedItem item = outputPairedIterable.get(i); + assertEquals(leftList.get(i), item.getLeftItem()); + assertEquals(rightList.get(i), item.getRightItem()); + } + } + + @Test + public void testStream() { + PairedIterable testIterable = PairedIterable.of(leftList, rightList); + + assertEquals( + leftList.subList(0, expectedIterableSize), + testIterable.stream().map(PairedItem::getLeftItem).collect(toList())); + assertEquals( + rightList.subList(0, expectedIterableSize), + testIterable.stream().map(PairedItem::getRightItem).collect(toList())); + } + + @Parameters(name = "{0}") + public static Object[][] testingParameters() { + return new Object[][] { + new Object[] { + "left iterable (int) larger than right iterable (string)", + LARGE_INTS_LIST, + SMALL_STRINGS_LIST, + SMALL_LIST_SIZE + }, + new Object[] { + "left iterable (string) larger than right iterable (int)", + LARGE_STRINGS_LIST, + SMALL_INTS_LIST, + SMALL_LIST_SIZE + }, + new Object[] { + "equal sized left and right (int, string)", + LARGE_INTS_LIST, + LARGE_STRINGS_LIST, + LARGE_LIST_SIZE + }, + new Object[] { + "equal sized left and right (string, int)", + SMALL_STRINGS_LIST, + SMALL_INTS_LIST, + SMALL_LIST_SIZE + }, + new Object[] { + "Left empty, right small list", Collections.emptyList(), LARGE_STRINGS_LIST, 0 + }, + new Object[] { + "Right empty, left small list", LARGE_STRINGS_LIST, Collections.emptyList(), 0 + }, + new Object[] { + "Right and left both empty lists", Collections.emptyList(), Collections.emptyList(), 0 + }, + }; + } + } + + @RunWith(JUnit4.class) + public static final class ErrorTest { + + @Test + public void leftIterableNullThrowsException() { + assertThrows(NullPointerException.class, () -> PairedIterable.of(null, SMALL_INTS_LIST)); + } + + @Test + public void rightIterableNullThrowsException() { + assertThrows(NullPointerException.class, () -> PairedIterable.of(SMALL_INTS_LIST, null)); + } + } + + private static List stringsList(int size) { + return IntStream.range(0, size) + .boxed() + .map(x -> UUID.randomUUID().toString()) + .collect(toList()); + } + + private static List intsList(int size) { + Random rnd = new Random(); + return IntStream.range(0, size).boxed().map(x -> rnd.nextInt()).collect(toList()); + } +} diff --git a/src/test/java/org/apache/commons/collections4/iterators/PairedIteratorTest.java b/src/test/java/org/apache/commons/collections4/iterators/PairedIteratorTest.java new file mode 100644 index 0000000000..32a4eba5e0 --- /dev/null +++ b/src/test/java/org/apache/commons/collections4/iterators/PairedIteratorTest.java @@ -0,0 +1,152 @@ +/* + * 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 static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.apache.commons.collections4.IteratorUtils; +import org.apache.commons.collections4.iterators.PairedIterator.PairedItem; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Random; +import java.util.UUID; + + +/** Unit test suite for {@link PairedIterator}. */ +public final class PairedIteratorTest + extends AbstractIteratorTest> { + + public PairedIteratorTest() { super(ObjectArrayIteratorTest.class.getSimpleName()); } + + private ArrayList smallStringsList; + private ArrayList largeStringsList; + private ArrayList smallIntsList; + private ArrayList largeIntsList; + + private static final int SMALL_LIST_SIZE = 20; + private static final int LARGE_LIST_SIZE = 40; + + + @BeforeEach + public void setUp() throws Exception { + + smallStringsList = new ArrayList<>(); + largeStringsList = new ArrayList<>(); + smallIntsList = new ArrayList<>(); + largeIntsList = new ArrayList<>(); + + Random random = new Random(); + + for (int i = 0; i < SMALL_LIST_SIZE; i++) { + smallIntsList.add(random.nextInt()); + smallStringsList.add(UUID.randomUUID().toString()); + } + + for (int i = 0; i < LARGE_LIST_SIZE; i++) { + largeIntsList.add(random.nextInt()); + largeStringsList.add(UUID.randomUUID().toString()); + } + } + + @Override + public boolean supportsRemove() { + return false; + } + + @Override + public Iterator> makeEmptyIterator() { + return PairedIterator.of(IteratorUtils.emptyIterator(), IteratorUtils.emptyIterator()); + } + + @Override + public Iterator> makeObject() { + return PairedIterator.of(smallStringsList.iterator(), smallIntsList.iterator()); + } + + @Test + public void testLeftIteratorLargerThanRight() { + Iterator> zipPairIterator = + PairedIterator.ofIterables(largeStringsList, smallIntsList); + + + for (int i = 0; i < SMALL_LIST_SIZE; i++) { + assertTrue(zipPairIterator.hasNext()); + PairedItem zippedItem = zipPairIterator.next(); + + assertEquals(largeStringsList.get(i), zippedItem.getLeftItem()); + assertEquals(smallIntsList.get(i), zippedItem.getRightItem()); + } + + assertFalse(zipPairIterator.hasNext()); + } + + @Test + public void testRightIteratorLargerThanLeft() { + Iterator> zipPairIterator = + PairedIterator.ofIterables(smallStringsList, largeIntsList); + + + for (int i = 0; i < SMALL_LIST_SIZE; i++) { + assertTrue(zipPairIterator.hasNext()); + PairedItem zippedItem = zipPairIterator.next(); + + assertEquals(smallStringsList.get(i), zippedItem.getLeftItem()); + assertEquals(largeIntsList.get(i), zippedItem.getRightItem()); + } + + assertFalse(zipPairIterator.hasNext()); + } + + @Test + public void testEmptyLeftIterator() { + Iterator> zipPairIterator = + PairedIterator.of(IteratorUtils.emptyIterator(), largeIntsList.iterator()); + + assertFalse(zipPairIterator.hasNext()); + } + + @Test + public void testEmptyRightIterator() { + Iterator> zipPairIterator = + PairedIterator.of(largeStringsList.iterator(), IteratorUtils.emptyIterator()); + + assertFalse(zipPairIterator.hasNext()); + } + + @Test + public void testValidTupleString() { + Iterator> zipPairIterator = + PairedIterator.ofIterables(smallStringsList, largeIntsList); + + + for (int i = 0; i < SMALL_LIST_SIZE; i++) { + assertTrue(zipPairIterator.hasNext()); + PairedItem zippedItem = zipPairIterator.next(); + + assertEquals( + String.format("{%s, %s}", zippedItem.getLeftItem(), zippedItem.getRightItem()), + zippedItem.toString()); + } + + assertFalse(zipPairIterator.hasNext()); + } +} From 615d561f00b392f575ac7cd5e42f20d57718aed8 Mon Sep 17 00:00:00 2001 From: Anant Date: Mon, 28 Aug 2023 15:05:35 +0800 Subject: [PATCH 2/3] use single class imports --- .../commons/collections4/IteratorUtils.java | 34 ++++++++++++++++++- .../collections4/IteratorUtilsTest.java | 12 ++++++- .../collections4/PairedIterableTest.java | 8 +++-- 3 files changed, 49 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/apache/commons/collections4/IteratorUtils.java b/src/main/java/org/apache/commons/collections4/IteratorUtils.java index e6c2d4b008..f98e6a1ab4 100644 --- a/src/main/java/org/apache/commons/collections4/IteratorUtils.java +++ b/src/main/java/org/apache/commons/collections4/IteratorUtils.java @@ -30,7 +30,39 @@ import java.util.Objects; import org.apache.commons.collections4.functors.EqualPredicate; -import org.apache.commons.collections4.iterators.*; +import org.apache.commons.collections4.iterators.ArrayIterator; +import org.apache.commons.collections4.iterators.ArrayListIterator; +import org.apache.commons.collections4.iterators.BoundedIterator; +import org.apache.commons.collections4.iterators.CollatingIterator; +import org.apache.commons.collections4.iterators.EmptyIterator; +import org.apache.commons.collections4.iterators.EmptyListIterator; +import org.apache.commons.collections4.iterators.EmptyMapIterator; +import org.apache.commons.collections4.iterators.EmptyOrderedIterator; +import org.apache.commons.collections4.iterators.EmptyOrderedMapIterator; +import org.apache.commons.collections4.iterators.EnumerationIterator; +import org.apache.commons.collections4.iterators.FilterIterator; +import org.apache.commons.collections4.iterators.FilterListIterator; +import org.apache.commons.collections4.iterators.IteratorChain; +import org.apache.commons.collections4.iterators.IteratorEnumeration; +import org.apache.commons.collections4.iterators.IteratorIterable; +import org.apache.commons.collections4.iterators.ListIteratorWrapper; +import org.apache.commons.collections4.iterators.LoopingIterator; +import org.apache.commons.collections4.iterators.LoopingListIterator; +import org.apache.commons.collections4.iterators.NodeListIterator; +import org.apache.commons.collections4.iterators.ObjectArrayIterator; +import org.apache.commons.collections4.iterators.ObjectArrayListIterator; +import org.apache.commons.collections4.iterators.ObjectGraphIterator; +import org.apache.commons.collections4.iterators.PairedIterator; +import org.apache.commons.collections4.iterators.PeekingIterator; +import org.apache.commons.collections4.iterators.PushbackIterator; +import org.apache.commons.collections4.iterators.SingletonIterator; +import org.apache.commons.collections4.iterators.SingletonListIterator; +import org.apache.commons.collections4.iterators.SkippingIterator; +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; diff --git a/src/test/java/org/apache/commons/collections4/IteratorUtilsTest.java b/src/test/java/org/apache/commons/collections4/IteratorUtilsTest.java index b653ede93c..db3b7519b6 100644 --- a/src/test/java/org/apache/commons/collections4/IteratorUtilsTest.java +++ b/src/test/java/org/apache/commons/collections4/IteratorUtilsTest.java @@ -46,7 +46,17 @@ import java.util.Set; import java.util.Vector; -import org.apache.commons.collections4.iterators.*; +import org.apache.commons.collections4.iterators.ArrayIterator; +import org.apache.commons.collections4.iterators.EmptyIterator; +import org.apache.commons.collections4.iterators.EmptyListIterator; +import org.apache.commons.collections4.iterators.EmptyMapIterator; +import org.apache.commons.collections4.iterators.EmptyOrderedIterator; +import org.apache.commons.collections4.iterators.EmptyOrderedMapIterator; +import org.apache.commons.collections4.iterators.EnumerationIterator; +import org.apache.commons.collections4.iterators.NodeListIterator; +import org.apache.commons.collections4.iterators.ObjectArrayIterator; +import org.apache.commons.collections4.iterators.PairedIterator; +import org.apache.commons.collections4.iterators.ZippingIterator; import org.apache.commons.collections4.map.EntrySetToMapIteratorAdapter; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/src/test/java/org/apache/commons/collections4/PairedIterableTest.java b/src/test/java/org/apache/commons/collections4/PairedIterableTest.java index 034ebac6a2..c9f8171ac5 100644 --- a/src/test/java/org/apache/commons/collections4/PairedIterableTest.java +++ b/src/test/java/org/apache/commons/collections4/PairedIterableTest.java @@ -24,7 +24,11 @@ import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Random; +import java.util.UUID; import java.util.stream.IntStream; import static java.util.Collections.unmodifiableList; @@ -47,14 +51,12 @@ public final class PairedIterableTest { @RunWith(Parameterized.class) public static final class ParameterizedTests { - private final String testCondition; private final List leftList; private final List rightList; private final int expectedIterableSize; public ParameterizedTests( String testCondition, List leftList, List rightList, int expectedIterableSize) { - this.testCondition = testCondition; this.leftList = leftList; this.rightList = rightList; this.expectedIterableSize = expectedIterableSize; From c688b652941c31b99fe48697d1e130c0f41afe11 Mon Sep 17 00:00:00 2001 From: Anant Date: Thu, 31 Aug 2023 12:50:04 +0800 Subject: [PATCH 3/3] fix checkstyle error --- .../collections4/PairedIterableTest.java | 88 +++++++++---------- .../iterators/PairedIteratorTest.java | 33 ++++--- 2 files changed, 63 insertions(+), 58 deletions(-) diff --git a/src/test/java/org/apache/commons/collections4/PairedIterableTest.java b/src/test/java/org/apache/commons/collections4/PairedIterableTest.java index c9f8171ac5..a3e0594029 100644 --- a/src/test/java/org/apache/commons/collections4/PairedIterableTest.java +++ b/src/test/java/org/apache/commons/collections4/PairedIterableTest.java @@ -43,9 +43,9 @@ public final class PairedIterableTest { private static final int LARGE_LIST_SIZE = 40; private static final List SMALL_STRINGS_LIST = - unmodifiableList(stringsList(SMALL_LIST_SIZE)); + unmodifiableList(stringsList(SMALL_LIST_SIZE)); private static final List LARGE_STRINGS_LIST = - unmodifiableList(stringsList(LARGE_LIST_SIZE)); + unmodifiableList(stringsList(LARGE_LIST_SIZE)); private static final List SMALL_INTS_LIST = unmodifiableList(intsList(SMALL_LIST_SIZE)); private static final List LARGE_INTS_LIST = unmodifiableList(intsList(LARGE_LIST_SIZE)); @@ -56,7 +56,7 @@ public static final class ParameterizedTests { private final int expectedIterableSize; public ParameterizedTests( - String testCondition, List leftList, List rightList, int expectedIterableSize) { + String testCondition, List leftList, List rightList, int expectedIterableSize) { this.leftList = leftList; this.rightList = rightList; this.expectedIterableSize = expectedIterableSize; @@ -83,49 +83,49 @@ public void testStream() { PairedIterable testIterable = PairedIterable.of(leftList, rightList); assertEquals( - leftList.subList(0, expectedIterableSize), - testIterable.stream().map(PairedItem::getLeftItem).collect(toList())); + leftList.subList(0, expectedIterableSize), + testIterable.stream().map(PairedItem::getLeftItem).collect(toList())); assertEquals( - rightList.subList(0, expectedIterableSize), - testIterable.stream().map(PairedItem::getRightItem).collect(toList())); + rightList.subList(0, expectedIterableSize), + testIterable.stream().map(PairedItem::getRightItem).collect(toList())); } @Parameters(name = "{0}") public static Object[][] testingParameters() { - return new Object[][] { - new Object[] { - "left iterable (int) larger than right iterable (string)", - LARGE_INTS_LIST, - SMALL_STRINGS_LIST, - SMALL_LIST_SIZE - }, - new Object[] { - "left iterable (string) larger than right iterable (int)", - LARGE_STRINGS_LIST, - SMALL_INTS_LIST, - SMALL_LIST_SIZE - }, - new Object[] { - "equal sized left and right (int, string)", - LARGE_INTS_LIST, - LARGE_STRINGS_LIST, - LARGE_LIST_SIZE - }, - new Object[] { - "equal sized left and right (string, int)", - SMALL_STRINGS_LIST, - SMALL_INTS_LIST, - SMALL_LIST_SIZE - }, - new Object[] { - "Left empty, right small list", Collections.emptyList(), LARGE_STRINGS_LIST, 0 - }, - new Object[] { - "Right empty, left small list", LARGE_STRINGS_LIST, Collections.emptyList(), 0 - }, - new Object[] { - "Right and left both empty lists", Collections.emptyList(), Collections.emptyList(), 0 - }, + return new Object[][]{ + new Object[]{ + "left iterable (int) larger than right iterable (string)", + LARGE_INTS_LIST, + SMALL_STRINGS_LIST, + SMALL_LIST_SIZE + }, + new Object[]{ + "left iterable (string) larger than right iterable (int)", + LARGE_STRINGS_LIST, + SMALL_INTS_LIST, + SMALL_LIST_SIZE + }, + new Object[]{ + "equal sized left and right (int, string)", + LARGE_INTS_LIST, + LARGE_STRINGS_LIST, + LARGE_LIST_SIZE + }, + new Object[]{ + "equal sized left and right (string, int)", + SMALL_STRINGS_LIST, + SMALL_INTS_LIST, + SMALL_LIST_SIZE + }, + new Object[]{ + "Left empty, right small list", Collections.emptyList(), LARGE_STRINGS_LIST, 0 + }, + new Object[]{ + "Right empty, left small list", LARGE_STRINGS_LIST, Collections.emptyList(), 0 + }, + new Object[]{ + "Right and left both empty lists", Collections.emptyList(), Collections.emptyList(), 0 + }, }; } } @@ -146,9 +146,9 @@ public void rightIterableNullThrowsException() { private static List stringsList(int size) { return IntStream.range(0, size) - .boxed() - .map(x -> UUID.randomUUID().toString()) - .collect(toList()); + .boxed() + .map(x -> UUID.randomUUID().toString()) + .collect(toList()); } private static List intsList(int size) { diff --git a/src/test/java/org/apache/commons/collections4/iterators/PairedIteratorTest.java b/src/test/java/org/apache/commons/collections4/iterators/PairedIteratorTest.java index 32a4eba5e0..b45ab72999 100644 --- a/src/test/java/org/apache/commons/collections4/iterators/PairedIteratorTest.java +++ b/src/test/java/org/apache/commons/collections4/iterators/PairedIteratorTest.java @@ -14,11 +14,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.commons.collections4.iterators; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +package org.apache.commons.collections4.iterators; import org.apache.commons.collections4.IteratorUtils; import org.apache.commons.collections4.iterators.PairedIterator.PairedItem; @@ -30,12 +27,20 @@ import java.util.Random; import java.util.UUID; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + -/** Unit test suite for {@link PairedIterator}. */ +/** + * Unit test suite for {@link PairedIterator}. + */ public final class PairedIteratorTest - extends AbstractIteratorTest> { + extends AbstractIteratorTest> { - public PairedIteratorTest() { super(ObjectArrayIteratorTest.class.getSimpleName()); } + public PairedIteratorTest() { + super(ObjectArrayIteratorTest.class.getSimpleName()); + } private ArrayList smallStringsList; private ArrayList largeStringsList; @@ -85,7 +90,7 @@ public Iterator> makeObject() { @Test public void testLeftIteratorLargerThanRight() { Iterator> zipPairIterator = - PairedIterator.ofIterables(largeStringsList, smallIntsList); + PairedIterator.ofIterables(largeStringsList, smallIntsList); for (int i = 0; i < SMALL_LIST_SIZE; i++) { @@ -102,7 +107,7 @@ public void testLeftIteratorLargerThanRight() { @Test public void testRightIteratorLargerThanLeft() { Iterator> zipPairIterator = - PairedIterator.ofIterables(smallStringsList, largeIntsList); + PairedIterator.ofIterables(smallStringsList, largeIntsList); for (int i = 0; i < SMALL_LIST_SIZE; i++) { @@ -119,7 +124,7 @@ public void testRightIteratorLargerThanLeft() { @Test public void testEmptyLeftIterator() { Iterator> zipPairIterator = - PairedIterator.of(IteratorUtils.emptyIterator(), largeIntsList.iterator()); + PairedIterator.of(IteratorUtils.emptyIterator(), largeIntsList.iterator()); assertFalse(zipPairIterator.hasNext()); } @@ -127,7 +132,7 @@ public void testEmptyLeftIterator() { @Test public void testEmptyRightIterator() { Iterator> zipPairIterator = - PairedIterator.of(largeStringsList.iterator(), IteratorUtils.emptyIterator()); + PairedIterator.of(largeStringsList.iterator(), IteratorUtils.emptyIterator()); assertFalse(zipPairIterator.hasNext()); } @@ -135,7 +140,7 @@ public void testEmptyRightIterator() { @Test public void testValidTupleString() { Iterator> zipPairIterator = - PairedIterator.ofIterables(smallStringsList, largeIntsList); + PairedIterator.ofIterables(smallStringsList, largeIntsList); for (int i = 0; i < SMALL_LIST_SIZE; i++) { @@ -143,8 +148,8 @@ public void testValidTupleString() { PairedItem zippedItem = zipPairIterator.next(); assertEquals( - String.format("{%s, %s}", zippedItem.getLeftItem(), zippedItem.getRightItem()), - zippedItem.toString()); + String.format("{%s, %s}", zippedItem.getLeftItem(), zippedItem.getRightItem()), + zippedItem.toString()); } assertFalse(zipPairIterator.hasNext());