From e100c136f4859cd6c0ea433fbac85345e1d3c7bf Mon Sep 17 00:00:00 2001 From: Anant Date: Wed, 29 May 2024 22:37:22 +0800 Subject: [PATCH] Added Paired Iterator fix review comments rebase on master fix checkstyle error use single class imports --- pom.xml | 3 + .../commons/collections4/IterableUtils.java | 33 ++++ .../commons/collections4/IteratorUtils.java | 21 +++ .../commons/collections4/PairedIterable.java | 100 +++++++++++ .../iterators/PairedIterator.java | 169 ++++++++++++++++++ .../collections4/IteratorUtilsTest.java | 8 + .../collections4/PairedIterableTest.java | 156 ++++++++++++++++ .../iterators/PairedIteratorTest.java | 157 ++++++++++++++++ 8 files changed, 647 insertions(+) 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 26b0d707ad..f25c6a92e5 100644 --- a/pom.xml +++ b/pom.xml @@ -809,6 +809,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 9c5d58336d..6c2a490f5d 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; @@ -622,6 +623,38 @@ public static boolean matchesAny(final Iterable iterable, final Predicate return IteratorUtils.matchesAny(emptyIteratorIfNull(iterable), predicate); } + /** + * 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()); + } + }; + } + /** * Partitions all elements from iterable into separate output collections, * based on the evaluation of the given predicates. diff --git a/src/main/java/org/apache/commons/collections4/IteratorUtils.java b/src/main/java/org/apache/commons/collections4/IteratorUtils.java index 5918e8c7ab..ffa6b52721 100644 --- a/src/main/java/org/apache/commons/collections4/IteratorUtils.java +++ b/src/main/java/org/apache/commons/collections4/IteratorUtils.java @@ -52,6 +52,7 @@ 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; @@ -1056,6 +1057,26 @@ public static Iterator objectGraphIterator(final E root, return new ObjectGraphIterator<>(root, transformer); } + /** + * 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 + * @since 4.5 + */ + public static PairedIterator pairedIterator(final Iterator left, Iterator right) { + return PairedIterator.of(left, right); + } + /** * Gets an iterator that supports one-element lookahead. * 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..22895f2016 --- /dev/null +++ b/src/main/java/org/apache/commons/collections4/PairedIterable.java @@ -0,0 +1,100 @@ +/* + * 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 + * @since 4.5 + */ +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; + + /** + * 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 + * @since 4.5 + */ + 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 + * @since 4.5 + */ + public static PairedIterable of(Iterable leftIterable, Iterable rightIterable) { + return new PairedIterable<>(leftIterable, rightIterable); + } + + @Override + public Iterator> iterator() { + return PairedIterator.ofIterables(leftIterable, rightIterable); + } + + public Stream> stream() { + return StreamSupport.stream(spliterator(), 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..6d45c34bdd --- /dev/null +++ b/src/main/java/org/apache/commons/collections4/iterators/PairedIterator.java @@ -0,0 +1,169 @@ +/* + * 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 = pairedIterator.next();
+ *     ...
+ *   }
+ * }
+ * + * @param the left elements' type + * @param the right elements' type + * @since 4.5 + */ +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; + + /** + * 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 + * @since 4.5 + */ + 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 + * @since 4.5 + */ + 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 + * @since 4.5 + */ + public static PairedIterator ofIterables(Iterable leftIterable, Iterable rightIterable) { + return of(requireNonNull(leftIterable).iterator(), requireNonNull(rightIterable).iterator()); + } + + /** + * 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 + * @since 4.5 + */ + 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 aafbacaf12..12d2fa9704 100644 --- a/src/test/java/org/apache/commons/collections4/IteratorUtilsTest.java +++ b/src/test/java/org/apache/commons/collections4/IteratorUtilsTest.java @@ -55,6 +55,7 @@ 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; @@ -1116,4 +1117,11 @@ public void testZippingIterator() { assertTrue(IteratorUtils.zippingIterator(iterator, iterator) 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..c85c82ff4f --- /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.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; +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 List leftList; + private final List rightList; + private final int expectedIterableSize; + + public ParameterizedTests( + String testCondition, List leftList, List rightList, int expectedIterableSize) { + 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..b45ab72999 --- /dev/null +++ b/src/test/java/org/apache/commons/collections4/iterators/PairedIteratorTest.java @@ -0,0 +1,157 @@ +/* + * 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 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; + +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}. + */ +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()); + } +}