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:
Thomas Neidhart 2015-05-27 21:18:19 +00:00
parent db0582a60e
commit e57282bd9c
3 changed files with 356 additions and 6 deletions

View File

@ -18,6 +18,7 @@ package org.apache.commons.collections4;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Comparator;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
@ -151,6 +152,55 @@ public class FluentIterable<E> implements Iterable<E> {
return of(IterableUtils.chainedIterable(iterable, other)); 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 * This method fully traverses an iterator of this iterable and returns
* a new iterable with the same contents, but without any reference * 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)); 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 * Returns a new FluentIterable whose iterator will skip the first
* N elements from this iterable. * N elements from this iterable.
@ -237,6 +297,35 @@ public class FluentIterable<E> implements Iterable<E> {
return of(IterableUtils.uniqueIterable(iterable)); 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 // convenience methods
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
@ -357,10 +446,10 @@ public class FluentIterable<E> implements Iterable<E> {
} }
/** /**
* Returns a list containing all elements of this iterable by traversing * Returns a list containing all elements of this iterable by
* its iterator. * traversing its iterator.
* <p> * <p>
* The returned list is mutable. * The returned list is guaranteed to be mutable.
* *
* @return a list of the iterable contents * @return a list of the iterable contents
*/ */

View File

@ -17,11 +17,14 @@
package org.apache.commons.collections4; package org.apache.commons.collections4;
import java.util.Collection; import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import org.apache.commons.collections4.iterators.LazyIteratorChain; 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.UniqueFilterIterator;
import org.apache.commons.collections4.iterators.ZippingIterator;
/** /**
* Provides utility methods and decorators for {@link Iterable} instances. * 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 // Filtered
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
@ -151,7 +185,8 @@ public class IterableUtils {
* @return a filtered view on the specified iterable * @return a filtered view on the specified iterable
* @throws NullPointerException if predicate is null * @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) { if (predicate == null) {
throw new NullPointerException("predicate must not be 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 // Skipping
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
@ -276,7 +343,8 @@ public class IterableUtils {
* @return a transformed view of the specified iterable * @return a transformed view of the specified iterable
* @throws NullPointerException if transformer is null * @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) { if (transformer == null) {
throw new NullPointerException("transformer must not be 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 // Utility methods
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------

View File

@ -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;
}
}