Skip to content

Commit e100c13

Browse files
committed
Added Paired Iterator
fix review comments rebase on master fix checkstyle error use single class imports
1 parent 66e2c57 commit e100c13

8 files changed

Lines changed: 647 additions & 0 deletions

File tree

pom.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -809,6 +809,9 @@
809809
<contributor>
810810
<name>Arturo Bernal</name>
811811
</contributor>
812+
<contributor>
813+
<name>Anant Damle</name>
814+
</contributor>
812815
</contributors>
813816

814817
</project>

src/main/java/org/apache/commons/collections4/IterableUtils.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
import org.apache.commons.collections4.functors.EqualPredicate;
2929
import org.apache.commons.collections4.iterators.LazyIteratorChain;
30+
import org.apache.commons.collections4.iterators.PairedIterator.PairedItem;
3031
import org.apache.commons.collections4.iterators.ReverseListIterator;
3132
import org.apache.commons.collections4.iterators.UniqueFilterIterator;
3233

@@ -622,6 +623,38 @@ public static <E> boolean matchesAny(final Iterable<E> iterable, final Predicate
622623
return IteratorUtils.matchesAny(emptyIteratorIfNull(iterable), predicate);
623624
}
624625

626+
/**
627+
* Provides iteration over the elements contained in a pair of Iterables in-tandem.
628+
* <p>
629+
* The returned iterable has an iterator that traverses the elements in {@code a}
630+
* and {@code b} together until one of the iterables is traversed completely.
631+
* </p>
632+
*
633+
* <p>
634+
* The returned iterable's iterator does NOT support {@code remove()}.
635+
* </p>
636+
*
637+
* @param <L> the left elements' type
638+
* @param <R> the right elements' type
639+
* @param left the iterable for the left side elements
640+
* @param right the iterable for the right side elements
641+
* @return an iterable, over the decorated iterables to traverse them together until one is
642+
* exhausted
643+
* @throws NullPointerException if any iterator is null
644+
* @since 4.5
645+
*/
646+
public static <L, R> Iterable<PairedItem<L, R>> pairedIterable(final Iterable<L> left, final Iterable<R> right) {
647+
checkNotNull(left);
648+
checkNotNull(right);
649+
650+
return new FluentIterable<PairedItem<L, R>>(){
651+
@Override
652+
public Iterator<PairedItem<L, R>> iterator() {
653+
return IteratorUtils.pairedIterator(left.iterator(), right.iterator());
654+
}
655+
};
656+
}
657+
625658
/**
626659
* Partitions all elements from iterable into separate output collections,
627660
* based on the evaluation of the given predicates.

src/main/java/org/apache/commons/collections4/IteratorUtils.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import org.apache.commons.collections4.iterators.ObjectArrayIterator;
5353
import org.apache.commons.collections4.iterators.ObjectArrayListIterator;
5454
import org.apache.commons.collections4.iterators.ObjectGraphIterator;
55+
import org.apache.commons.collections4.iterators.PairedIterator;
5556
import org.apache.commons.collections4.iterators.PeekingIterator;
5657
import org.apache.commons.collections4.iterators.PushbackIterator;
5758
import org.apache.commons.collections4.iterators.SingletonIterator;
@@ -1056,6 +1057,26 @@ public static <E> Iterator<E> objectGraphIterator(final E root,
10561057
return new ObjectGraphIterator<>(root, transformer);
10571058
}
10581059

1060+
/**
1061+
* Gets an Iterator over the elements contained in a pair of Iterables in-tandem.
1062+
* <p>
1063+
* The returned iterator traverses the elements in {@code a} and {@code b} together until one of the iterators
1064+
* is exhausted.
1065+
* <p>
1066+
* The returned iterator does NOT support {@code remove()}.
1067+
*
1068+
* @param <L> the left elements' type
1069+
* @param <R> the right elements' type
1070+
* @param left the iterator for the left side elements
1071+
* @param right the iterator for the right side elements
1072+
* @return an iterator, to iterate over the decorated iterators together until one is exhausted
1073+
* @throws NullPointerException if any iterator is null
1074+
* @since 4.5
1075+
*/
1076+
public static <L, R> PairedIterator<L, R> pairedIterator(final Iterator<L> left, Iterator<R> right) {
1077+
return PairedIterator.of(left, right);
1078+
}
1079+
10591080
/**
10601081
* Gets an iterator that supports one-element lookahead.
10611082
*
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.commons.collections4;
18+
19+
import org.apache.commons.collections4.iterators.PairedIterator;
20+
import org.apache.commons.collections4.iterators.PairedIterator.PairedItem;
21+
22+
import java.util.Iterator;
23+
import java.util.stream.Stream;
24+
import java.util.stream.StreamSupport;
25+
26+
import static java.util.Objects.requireNonNull;
27+
28+
/**
29+
* Provides iteration over the elements contained in a pair of Iterables in-tandem.
30+
*
31+
* <p>
32+
* Given two {@link Iterable} instances {@code A} and {@code B}, the {@link #iterator} method on this
33+
* iterator provide a Pair of {@code A.next()} and {@code B.next()} until one of the iterators is
34+
* exhausted.
35+
* </p>
36+
* This can simplify zipping over two iterables using the for-each construct.
37+
* Example usage:
38+
* <pre>{@code
39+
* List<Integer> studentIds = ...
40+
* List<String> studentNames = ...
41+
*
42+
* for (PairedItem<Integer, String> items : PairedIterable.of(studentIds, studentNames) {
43+
* Integer studentId = item.getLeft();
44+
* String studentName = item.getRight();
45+
* ...
46+
* }
47+
* }</pre>
48+
*
49+
* @param <L> the left elements' type
50+
* @param <R> the right elements' type
51+
* @since 4.5
52+
*/
53+
public class PairedIterable<L, R> implements Iterable<PairedItem<L, R>> {
54+
55+
/**
56+
* The left {@link Iterable}s to evaluate.
57+
*/
58+
private final Iterable<L> leftIterable;
59+
60+
/**
61+
* The right {@link Iterable}s to evaluate.
62+
*/
63+
private final Iterable<R> rightIterable;
64+
65+
/**
66+
* Constructs a new {@code PairedIterable} that will provide iteration over two given iterables.
67+
*
68+
* @param leftIterable the iterable for the left side element.
69+
* @param rightIterable the iterable for the right side element.
70+
* @throws NullPointerException if either iterator is null
71+
* @since 4.5
72+
*/
73+
public PairedIterable(Iterable<L> leftIterable, Iterable<R> rightIterable) {
74+
this.leftIterable = requireNonNull(leftIterable);
75+
this.rightIterable = requireNonNull(rightIterable);
76+
}
77+
78+
/**
79+
* Convenience static factory to construct the PairedIterable from provided
80+
* {@link Iterable} sources.
81+
*
82+
* @param leftIterable the iterable for the left side element.
83+
* @param rightIterable the iterable for the right side element.
84+
* @return the Iterable to iterate over the elements derived from the provided iterables.
85+
* @throws NullPointerException if either iterables is null
86+
* @since 4.5
87+
*/
88+
public static <L, R> PairedIterable<L, R> of(Iterable<L> leftIterable, Iterable<R> rightIterable) {
89+
return new PairedIterable<>(leftIterable, rightIterable);
90+
}
91+
92+
@Override
93+
public Iterator<PairedItem<L, R>> iterator() {
94+
return PairedIterator.ofIterables(leftIterable, rightIterable);
95+
}
96+
97+
public Stream<PairedItem<L, R>> stream() {
98+
return StreamSupport.stream(spliterator(), false);
99+
}
100+
}
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.commons.collections4.iterators;
18+
19+
import static java.util.Objects.requireNonNull;
20+
21+
import java.util.Iterator;
22+
import java.util.NoSuchElementException;
23+
import org.apache.commons.collections4.iterators.PairedIterator.PairedItem;
24+
25+
/**
26+
* Provides a iteration over the elements contained in a pair of Iterators.
27+
*
28+
* <p>
29+
* Given two {@link Iterator} instances {@code A} and {@code B}, the {@link #next} method on this
30+
* iterator provide a Pair of {@code A.next()} and {@code B.next()} until one of the iterators is
31+
* exhausted.
32+
* </p>
33+
* Example usage:
34+
* <pre>{@code
35+
* List<Integer> studentIds = ...
36+
* List<String> studentNames = ...
37+
*
38+
* PairedIterator<PairedItem<Integer, String>> pairedIterator =
39+
* PairedIterator.ofIterables(studentIds, studentNames);
40+
*
41+
* while (pairedIterator.hasNext()) {
42+
* PairedItem<Integer, String> item = pairedIterator.next();
43+
* ...
44+
* }
45+
* }</pre>
46+
*
47+
* @param <L> the left elements' type
48+
* @param <R> the right elements' type
49+
* @since 4.5
50+
*/
51+
public class PairedIterator<L, R> implements Iterator<PairedItem<L, R>> {
52+
53+
/**
54+
* The left {@link Iterator}s to evaluate.
55+
*/
56+
private final Iterator<L> leftIterator;
57+
58+
/**
59+
* The right {@link Iterator}s to evaluate.
60+
*/
61+
private final Iterator<R> rightIterator;
62+
63+
/**
64+
* Constructs a new {@code ZipPairIterator} that will provide iteration over the two given
65+
* iterators.
66+
*
67+
* @param leftIterator the iterator for the left side element.
68+
* @param rightIterator the iterator for the right side element.
69+
* @throws NullPointerException if either iterator is null
70+
* @since 4.5
71+
*/
72+
public PairedIterator(Iterator<L> leftIterator, Iterator<R> rightIterator) {
73+
this.leftIterator = requireNonNull(leftIterator);
74+
this.rightIterator = requireNonNull(rightIterator);
75+
}
76+
77+
/**
78+
* Convenience static factory to construct the ZipPairIterator
79+
*
80+
* @param leftIterator the iterator for the left side element.
81+
* @param rightIterator the iterator for the right side element.
82+
* @return the iterator to iterate over the provided iterators.
83+
* @throws NullPointerException if either iterator is null
84+
* @since 4.5
85+
*/
86+
public static <L, R> PairedIterator<L, R> of(Iterator<L> leftIterator, Iterator<R> rightIterator) {
87+
return new PairedIterator<>(leftIterator, rightIterator);
88+
}
89+
90+
/**
91+
* Convenience static factory to construct the ZipPairIterator from any {@link Iterable} sources.
92+
*
93+
* @param leftIterable the iterable for the left side element.
94+
* @param rightIterable the iterable for the right side element.
95+
* @return the iterator to iterate over the iterators derived from the provided iterables.
96+
* @throws NullPointerException if either iterables is null
97+
* @since 4.5
98+
*/
99+
public static <L, R> PairedIterator<L, R> ofIterables(Iterable<L> leftIterable, Iterable<R> rightIterable) {
100+
return of(requireNonNull(leftIterable).iterator(), requireNonNull(rightIterable).iterator());
101+
}
102+
103+
/**
104+
* Returns {@code true} if both the child iterators have remaining elements.
105+
*
106+
* @return true if both the child iterators have remaining elements
107+
*/
108+
@Override
109+
public boolean hasNext() {
110+
return leftIterator.hasNext() && rightIterator.hasNext();
111+
}
112+
113+
/**
114+
* Returns the next elements from both the child iterators.
115+
*
116+
* @return the next elements from both the iterators.
117+
* @throws NoSuchElementException if any one child iterator is exhausted.
118+
*/
119+
@Override
120+
public PairedItem<L, R> next() {
121+
if (!hasNext()) {
122+
throw new NoSuchElementException();
123+
}
124+
return PairedItem.of(leftIterator.next(), rightIterator.next());
125+
}
126+
127+
/**
128+
* An immutable tuple class to represent elements from both the iterators.
129+
*
130+
* @param <L> the left elements' type
131+
* @param <R> the right elements' type
132+
* @since 4.5
133+
*/
134+
public static final class PairedItem<L, R> {
135+
136+
private final L leftItem;
137+
138+
private final R rightItem;
139+
140+
private PairedItem(L leftItem, R rightItem) {
141+
this.leftItem = leftItem;
142+
this.rightItem = rightItem;
143+
}
144+
145+
/**
146+
* Convenience static factory method to construct the tuple pair.
147+
*
148+
* @param left the left element
149+
* @param right the right element
150+
* @return the Immutable tuple pair of two elements.
151+
*/
152+
private static <L, R> PairedItem<L, R> of(L left, R right) {
153+
return new PairedItem<>(left, right);
154+
}
155+
156+
public L getLeftItem() {
157+
return leftItem;
158+
}
159+
160+
public R getRightItem() {
161+
return rightItem;
162+
}
163+
164+
@Override
165+
public String toString() {
166+
return String.format("{%s, %s}", leftItem, rightItem);
167+
}
168+
}
169+
}

src/test/java/org/apache/commons/collections4/IteratorUtilsTest.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
import org.apache.commons.collections4.iterators.EnumerationIterator;
5656
import org.apache.commons.collections4.iterators.NodeListIterator;
5757
import org.apache.commons.collections4.iterators.ObjectArrayIterator;
58+
import org.apache.commons.collections4.iterators.PairedIterator;
5859
import org.apache.commons.collections4.iterators.ZippingIterator;
5960
import org.apache.commons.collections4.map.EntrySetToMapIteratorAdapter;
6061
import org.junit.jupiter.api.BeforeEach;
@@ -1116,4 +1117,11 @@ public void testZippingIterator() {
11161117
assertTrue(IteratorUtils.zippingIterator(iterator, iterator) instanceof ZippingIterator, "create instance fail");
11171118
}
11181119

1120+
@Test
1121+
public void testPairedIterator() {
1122+
final ArrayList<String> stringList = new ArrayList<>();
1123+
final ArrayList<Integer> integerList = new ArrayList<>();
1124+
1125+
assertTrue(IteratorUtils.pairedIterator(stringList.iterator(), integerList.iterator()) instanceof PairedIterator, "create instance failed");
1126+
}
11191127
}

0 commit comments

Comments
 (0)