Add collating, zipping, reversing methods to FluentIterable, add ZippingIterator.
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/collections/trunk@1682121 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
db0582a60e
commit
e57282bd9c
|
@ -18,6 +18,7 @@ package org.apache.commons.collections4;
|
|||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
@ -151,6 +152,55 @@ public class FluentIterable<E> implements Iterable<E> {
|
|||
return of(IterableUtils.chainedIterable(iterable, other));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new FluentIterable whose iterator will traverse the
|
||||
* elements of the current and provided iterable in natural order.
|
||||
* <p>
|
||||
* Example: natural ordering
|
||||
* <ul>
|
||||
* <li>this contains elements [1, 3, 5, 7]
|
||||
* <li>other contains elements [2, 4, 6, 8]
|
||||
* </ul>
|
||||
* <p>
|
||||
* The returned iterable will traverse the elements in the following
|
||||
* order: [1, 2, 3, 4, 5, 6, 7, 8]
|
||||
* <p>
|
||||
* A <code>null</code> iterable will be treated as an empty iterable.
|
||||
*
|
||||
* @param other the other iterable to collate, may be null
|
||||
* @return a new iterable, collating this iterable with the other in natural order
|
||||
* @see {@link org.apache.commons.collections4.iterators.CollatingIterator CollatingIterator}
|
||||
*/
|
||||
public FluentIterable<E> collate(final Iterable<E> other) {
|
||||
return of(IterableUtils.collatedIterable(iterable, other, null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new FluentIterable whose iterator will traverse the
|
||||
* elements of the current and provided iterable according to the
|
||||
* ordering defined by an comparator.
|
||||
* <p>
|
||||
* Example: descending order
|
||||
* <ul>
|
||||
* <li>this contains elements [7, 5, 3, 1]
|
||||
* <li>other contains elements [8, 6, 4, 2]
|
||||
* </ul>
|
||||
* <p>
|
||||
* The returned iterable will traverse the elements in the following
|
||||
* order: [8, 7, 6, 5, 4, 3, 2, 1]
|
||||
* <p>
|
||||
* A <code>null</code> iterable will be treated as an empty iterable.
|
||||
*
|
||||
* @param other the other iterable to collate, may be null
|
||||
* @param comparator the comparator to define an ordering, may be null,
|
||||
* in which case natural ordering will be used
|
||||
* @return a new iterable, collating this iterable with the other in natural order
|
||||
* @see {@link org.apache.commons.collections4.iterators.CollatingIterator CollatingIterator}
|
||||
*/
|
||||
public FluentIterable<E> collate(final Iterable<E> other, Comparator<? super E> comparator) {
|
||||
return of(IterableUtils.collatedIterable(iterable, other, comparator));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method fully traverses an iterator of this iterable and returns
|
||||
* a new iterable with the same contents, but without any reference
|
||||
|
@ -202,6 +252,16 @@ public class FluentIterable<E> implements Iterable<E> {
|
|||
return of(IterableUtils.loopingIterable(iterable));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new FluentIterable whose iterator will traverse the
|
||||
* elements from this iterable in reverse order.
|
||||
*
|
||||
* @return a new iterable, providing a reversed view of this iterable
|
||||
*/
|
||||
public FluentIterable<E> reverse() {
|
||||
return of(IterableUtils.reversedIterable(iterable));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new FluentIterable whose iterator will skip the first
|
||||
* N elements from this iterable.
|
||||
|
@ -236,7 +296,36 @@ public class FluentIterable<E> implements Iterable<E> {
|
|||
public FluentIterable<E> unique() {
|
||||
return of(IterableUtils.uniqueIterable(iterable));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a new FluentIterable whose iterator will traverse
|
||||
* the elements of this iterable and the provided elements in
|
||||
* alternating order.
|
||||
*
|
||||
* @param elements the elements to interleave
|
||||
* @return a new iterable, interleaving this iterable with the elements
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public FluentIterable<E> zip(final E... elements) {
|
||||
return zip(Arrays.asList(elements));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new FluentIterable whose iterator will traverse
|
||||
* the elements of this iterable and the other iterable in
|
||||
* alternating order.
|
||||
*
|
||||
* @param other the other iterable to interleave
|
||||
* @return a new iterable, interleaving this iterable with others
|
||||
*/
|
||||
public FluentIterable<E> zip(final Iterable<E>... others) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Iterable<E>[] iterables = new Iterable[1 + others.length];
|
||||
iterables[0] = iterable;
|
||||
System.arraycopy(others, 0, iterables, 1, others.length);
|
||||
return of(IterableUtils.zippingIterable(iterables));
|
||||
}
|
||||
|
||||
// convenience methods
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
|
@ -357,10 +446,10 @@ public class FluentIterable<E> implements Iterable<E> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns a list containing all elements of this iterable by traversing
|
||||
* its iterator.
|
||||
* Returns a list containing all elements of this iterable by
|
||||
* traversing its iterator.
|
||||
* <p>
|
||||
* The returned list is mutable.
|
||||
* The returned list is guaranteed to be mutable.
|
||||
*
|
||||
* @return a list of the iterable contents
|
||||
*/
|
||||
|
|
|
@ -17,11 +17,14 @@
|
|||
package org.apache.commons.collections4;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.collections4.iterators.LazyIteratorChain;
|
||||
import org.apache.commons.collections4.iterators.ReverseListIterator;
|
||||
import org.apache.commons.collections4.iterators.UniqueFilterIterator;
|
||||
import org.apache.commons.collections4.iterators.ZippingIterator;
|
||||
|
||||
/**
|
||||
* Provides utility methods and decorators for {@link Iterable} instances.
|
||||
|
@ -136,6 +139,37 @@ public class IterableUtils {
|
|||
};
|
||||
}
|
||||
|
||||
// Collated
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Combines the two provided iterables into an ordered iterable using the
|
||||
* provided comparator. If the comparator is null, natural ordering will be
|
||||
* used.
|
||||
* <p>
|
||||
* The returned iterable's iterator supports {@code remove()} when the corresponding
|
||||
* input iterator supports it.
|
||||
*
|
||||
* @param <E> the element type
|
||||
* @param a the first iterable, may be null
|
||||
* @param b the second iterable, may be null
|
||||
* @param comparator the comparator defining an ordering over the elements,
|
||||
* may be null, in which case natural ordering will be used
|
||||
* @return a filtered view on the specified iterable
|
||||
*/
|
||||
public static <E> Iterable<E> collatedIterable(final Iterable<E> a,
|
||||
final Iterable<E> b,
|
||||
final Comparator<? super E> comparator) {
|
||||
return new FluentIterable<E>() {
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return IteratorUtils.collatedIterator(comparator,
|
||||
emptyIteratorIfNull(a),
|
||||
emptyIteratorIfNull(b));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Filtered
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
|
@ -151,7 +185,8 @@ public class IterableUtils {
|
|||
* @return a filtered view on the specified iterable
|
||||
* @throws NullPointerException if predicate is null
|
||||
*/
|
||||
public static <E> Iterable<E> filteredIterable(final Iterable<E> iterable, final Predicate<? super E> predicate) {
|
||||
public static <E> Iterable<E> filteredIterable(final Iterable<E> iterable,
|
||||
final Predicate<? super E> predicate) {
|
||||
if (predicate == null) {
|
||||
throw new NullPointerException("predicate must not be null.");
|
||||
}
|
||||
|
@ -232,6 +267,38 @@ public class IterableUtils {
|
|||
};
|
||||
}
|
||||
|
||||
// Reversed
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns a reversed view of the given iterable.
|
||||
* <p>
|
||||
* In case the provided iterable is a {@link List} instance, a
|
||||
* {@link ReverseListIterator} will be used to reverse the traversal
|
||||
* order, otherwise an intermediate {@link List} needs to be
|
||||
* created.
|
||||
* <p>
|
||||
* The returned iterable's iterator supports {@code remove()} if the
|
||||
* provided iterable is a {@link List} instance.
|
||||
*
|
||||
* @param <E> the element type
|
||||
* @param iterable the iterable to use, may be null
|
||||
* @return a reversed view of the specified iterable
|
||||
* @see ReverseListIterator
|
||||
*/
|
||||
public static <E> Iterable<E> reversedIterable(final Iterable<E> iterable) {
|
||||
return new FluentIterable<E>() {
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
final List<E> list = (iterable instanceof List<?>) ?
|
||||
(List<E>) iterable :
|
||||
IteratorUtils.toList(emptyIteratorIfNull(iterable));
|
||||
|
||||
return new ReverseListIterator<E>(list);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Skipping
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
|
@ -276,7 +343,8 @@ public class IterableUtils {
|
|||
* @return a transformed view of the specified iterable
|
||||
* @throws NullPointerException if transformer is null
|
||||
*/
|
||||
public static <I, O> Iterable<O> transformedIterable(final Iterable<I> iterable, final Transformer<? super I, ? extends O> transformer) {
|
||||
public static <I, O> Iterable<O> transformedIterable(final Iterable<I> iterable,
|
||||
final Transformer<? super I, ? extends O> transformer) {
|
||||
if (transformer == null) {
|
||||
throw new NullPointerException("transformer must not be null.");
|
||||
}
|
||||
|
@ -310,6 +378,58 @@ public class IterableUtils {
|
|||
};
|
||||
}
|
||||
|
||||
// Zipping
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Interleaves two iterables into a single iterable.
|
||||
* <p>
|
||||
* The returned iterable has an iterator that traverses the elements in {@code a}
|
||||
* and {@code b} in alternating order. The source iterators are not polled until
|
||||
* necessary.
|
||||
* <p>
|
||||
* The returned iterable's iterator supports {@code remove()} when the corresponding
|
||||
* input iterator supports it.
|
||||
*
|
||||
* @param <E> the element type
|
||||
* @param a the first iterable
|
||||
* @param b the second iterable
|
||||
* @return a new iterable, interleaving the provided iterables
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <E> Iterable<E> zippingIterable(final Iterable<E> a, final Iterable<E> b) {
|
||||
return zippingIterable(new Iterable[] {a, b});
|
||||
}
|
||||
|
||||
/**
|
||||
* Interleaves two iterables into a single iterable.
|
||||
* <p>
|
||||
* The returned iterable has an iterator that traverses the elements in {@code a}
|
||||
* and {@code b} in alternating order. The source iterators are not polled until
|
||||
* necessary.
|
||||
* <p>
|
||||
* The returned iterable's iterator supports {@code remove()} when the corresponding
|
||||
* input iterator supports it.
|
||||
*
|
||||
* @param <E> the element type
|
||||
* @param a the first iterable
|
||||
* @param b the second iterable
|
||||
* @return a new iterable, interleaving the provided iterables
|
||||
*/
|
||||
public static <E> Iterable<E> zippingIterable(final Iterable<E>... iterables) {
|
||||
return new FluentIterable<E>() {
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
@SuppressWarnings("unchecked")
|
||||
Iterator<E>[] iterators = new Iterator[iterables.length];
|
||||
for (int i = 0; i < iterables.length; i++) {
|
||||
iterators[i] = emptyIteratorIfNull(iterables[i]);
|
||||
}
|
||||
return new ZippingIterator<E>(iterators);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Utility methods
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
|
|
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
* 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 java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
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>.
|
||||
*
|
||||
* @since 4.1
|
||||
* @version $Id$
|
||||
*/
|
||||
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;
|
||||
|
||||
// Constructors
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @throws NullPointerException if either iterator is null
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public ZippingIterator(final Iterator<? extends E> a, final Iterator<? extends E> b) {
|
||||
this(new Iterator[] {a, b});
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new <code>ZippingIterator</code> that will use the
|
||||
* specified comparator to provide ordered iteration over the array of
|
||||
* iterators.
|
||||
*
|
||||
* @param iterators the array of iterators
|
||||
* @throws NullPointerException if iterators array is or contains null
|
||||
*/
|
||||
public ZippingIterator(final Iterator<? extends E>... iterators) {
|
||||
// create a mutable list
|
||||
final List<Iterator<? extends E>> list = new ArrayList<Iterator<? extends E>>();
|
||||
for (Iterator<? extends E> iterator : iterators) {
|
||||
if (iterator == null) {
|
||||
throw new NullPointerException("Iterator must not be null");
|
||||
}
|
||||
list.add(iterator);
|
||||
}
|
||||
this.iterators = FluentIterable.of(list).loop().iterator();
|
||||
}
|
||||
|
||||
// Iterator Methods
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> 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
|
||||
if (nextIterator != null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
while(iterators.hasNext()) {
|
||||
final Iterator<? extends E> iterator = iterators.next();
|
||||
if (iterator.hasNext()) {
|
||||
nextIterator = iterator;
|
||||
return true;
|
||||
} else {
|
||||
// iterator is exhausted, remove it
|
||||
iterators.remove();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next element from a child iterator.
|
||||
*
|
||||
* @return the next interleaved element
|
||||
* @throws NoSuchElementException if no child iterator has any more elements
|
||||
*/
|
||||
public E next() throws NoSuchElementException {
|
||||
if (!hasNext()) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
|
||||
final E val = nextIterator.next();
|
||||
lastReturned = nextIterator;
|
||||
nextIterator = null;
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the last returned element from the child iterator that produced it.
|
||||
*
|
||||
* @throws IllegalStateException if there is no last returned element, or if
|
||||
* the last returned element has already been removed
|
||||
*/
|
||||
public void remove() {
|
||||
if (lastReturned == null) {
|
||||
throw new IllegalStateException("No value can be removed at present");
|
||||
}
|
||||
lastReturned.remove();
|
||||
lastReturned = null;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue