Skip to content

Commit

Permalink
Refactor ExtendedIterator and FilterIterator with a new interface
Browse files Browse the repository at this point in the history
IteratorOperations

- Inspired by Apache Jena's ExtendedIterator and Claude Warren's
ExtendedIterator
- Add missing Javadoc
  • Loading branch information
garydgregory committed Nov 1, 2024
1 parent c4a0d4c commit d591c85
Show file tree
Hide file tree
Showing 6 changed files with 252 additions and 105 deletions.
1 change: 1 addition & 0 deletions src/changes/changes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
<action type="fix" dev="ggregory" due-to="Gary Gregory">Add missing test AbstractIteratorTest.testForEachRemaining().</action>
<action type="fix" dev="ggregory" due-to="Gary Gregory, Claude Warren">Add FilterIterator.removeNext() #564.</action>
<action type="fix" issue="COLLECTIONS-870" dev="ggregory" due-to="Claude Warren, Gary Gregory">Added ExtendedIterator and tests #564.</action>
<action type="add" dev="ggregory" due-to="Gary Gregory">Refactor ExtendedIterator and FilterIterator with a new interface IteratorOperations.</action>
<!-- UPDATE -->
<action type="update" dev="ggregory" due-to="Gary Gregory">Bump org.apache.commons:commons-parent from 71 to 78 #534, #545, #550 #555, #566.</action>
<action type="update" issue="COLLECTIONS-857" dev="ggregory" due-to="Claude Warren">Update bloom filter documentation #508.</action>
Expand Down
28 changes: 28 additions & 0 deletions src/main/java/org/apache/commons/collections4/IteratorUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@
import java.util.ListIterator;
import java.util.Map;
import java.util.Objects;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.IntFunction;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import org.apache.commons.collections4.functors.EqualPredicate;
import org.apache.commons.collections4.iterators.ArrayIterator;
Expand Down Expand Up @@ -1226,6 +1230,30 @@ public static <E> SkippingIterator<E> skippingIterator(final Iterator<E> iterato
return new SkippingIterator<>(iterator, offset);
}

/**
* Creates a stream on the given Iterable.
*
* @param <E> the type of elements in the Iterable.
* @param iterable the Iterable to stream or null.
* @return a new Stream or {@link Stream#empty()} if the Iterable is null.
* @since 4.5.0-M3
*/
public static <E> Stream<E> stream(final Iterable<E> iterable) {
return iterable == null ? Stream.empty() : StreamSupport.stream(iterable.spliterator(), false);
}

/**
* Creates a stream on the given Iterator.
*
* @param <E> the type of elements in the Iterator.
* @param iterator the Iterator to stream or null.
* @return a new Stream or {@link Stream#empty()} if the Iterator is null.
* @since 4.5.0-M3
*/
public static <E> Stream<E> stream(final Iterator<E> iterator) {
return iterator == null ? Stream.empty() : StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.ORDERED), false);
}

/**
* Gets an array based on an iterator.
* <p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,32 +16,52 @@
*/
package org.apache.commons.collections4.iterators;

import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;


/**
* Extends Iterator functionality to include operations commonly found on streams (e.g. filtering, concatenating, mapping).
* It also provides convenience methods for common operations.
* Extends Iterator functionality to include operations commonly found on streams (e.g. filtering, concatenating, mapping). It also provides convenience methods
* for common operations.
*
* @param <T> The type of object returned from the iterator.
* @since 4.5.0-M3
*/
public final class ExtendedIterator<T> implements Iterator<T> {
public final class ExtendedIterator<T> implements IteratorOperations<T> {

/**
* Set to <code>true</code> if this wrapping doesn't permit the use of
* {@link #remove()}, otherwise removal is delegated to the base iterator.
* Create an ExtendedIterator returning the elements of <code>it</code>. If <code>it</code> is itself an ExtendedIterator, return that; otherwise wrap
* <code>it</code>.
*
* @param <T> The type of object returned from the iterator.
* @param it The iterator to wrap.
* @return An Extended iterator wrapping {@code it}
*/
private final boolean throwOnRemove;
public static <T> ExtendedIterator<T> create(final Iterator<T> it) {
return it instanceof ExtendedIterator<?> ? (ExtendedIterator<T>) it : new ExtendedIterator<>(it, false);
}

/**
* Creates an ExtendedIterator wrapped round <code>it</code>,
* which does not permit <code>.remove()</code>
* even if <code>it</code> does.
* Creates an ExtendedIterator wrapped round a {@link Stream}. The extended iterator does not permit <code>.remove()</code>.
* <p>
* The stream should not be used directly. The effect of doing so is undefined.
* </p>
*
* @param <T> The type of object returned from the iterator.
* @param stream the Stream to create an iterator from.
* @return an Extended iterator on the {@code stream} iterator.
*/
public static <T> ExtendedIterator<T> create(final Stream<T> stream) {
return new ExtendedIterator<>(stream.iterator(), true);
}

/**
* Creates an ExtendedIterator wrapped round <code>it</code>, which does not permit <code>.remove()</code> even if <code>it</code> does.
*
* @param <T> The type of object returned from the iterator.
* @param it The Iterator to wrap.
* @return an Extended iterator on {@code it}
* @throws UnsupportedOperationException if remove() is called on the resulting iterator.
Expand All @@ -51,22 +71,18 @@ public static <T> ExtendedIterator<T> createNoRemove(final Iterator<T> it) {
}

/**
* Creates an ExtendedIterator wrapped round a {@link Stream}.
* The extended iterator does not permit <code>.remove()</code>.
* <p>
* The stream should not be used directly. The effect of doing so is
* undefined.
* </p>
* @param stream the Stream to create an iterator from.
* @return an Extended iterator on the {@code stream} iterator.
* Creates an empty Extended iterator.
*
* @return An empty Extended iterator.
*/
public static <T> ExtendedIterator<T> create(final Stream<T> stream) {
return new ExtendedIterator<T>(stream.iterator(), true);
public static ExtendedIterator<?> emptyIterator() {
return new ExtendedIterator<>(Collections.emptyIterator(), false);
}

/**
* Flattens an iterator of iterators into an Iterator over the next level values.
* Similar to list splicing in lisp.
* Flattens an iterator of iterators into an Iterator over the next level values. Similar to list splicing in lisp.
*
* @param <T> The type of object returned from the iterator.
* @param iterators An iterator of iterators.
* @return An iterator over the logical concatenation of the inner iterators.
*/
Expand All @@ -82,74 +98,27 @@ protected Iterator<? extends T> nextIterator(final int count) {
}

/**
* Creates an empty Extended iterator.
* @return An empty Extended iterator.
*/
public static ExtendedIterator<?> emptyIterator() {
return new ExtendedIterator<>(Collections.emptyIterator(), false);
}

/**
* Create an ExtendedIterator returning the elements of <code>it</code>.
* If <code>it</code> is itself an ExtendedIterator, return that;
* otherwise wrap <code>it</code>.
* @param it The iterator to wrap.
* @return An Extended iterator wrapping {@code it}
* Set to <code>true</code> if this wrapping doesn't permit the use of {@link #remove()}, otherwise removal is delegated to the base iterator.
*/
public static <T> ExtendedIterator<T> create(final Iterator<T> it) {
return it instanceof ExtendedIterator<?>
? (ExtendedIterator<T>) it
: new ExtendedIterator<>(it, false);
}
private final boolean throwOnRemove;

/** the base iterator that we wrap */
/** The base iterator that we wrap */
private final Iterator<? extends T> base;

/**
* Initialise this wrapping with the given base iterator and remove-control.
* @param base the base iterator that this iterator wraps
* Initialize this wrapping with the given base iterator and remove-control.
*
* @param base the base iterator that this iterator wraps
* @param throwOnRemove true if .remove() must throw an exception
*/
private ExtendedIterator(final Iterator<? extends T> base, final boolean throwOnRemove) {
this.base = base;
this.throwOnRemove = throwOnRemove;
}

@Override
public boolean hasNext() {
return base.hasNext();
}

@Override
public T next() {
return base.next();
}

@Override
public void forEachRemaining(final Consumer<? super T> action) {
base.forEachRemaining(action);
}

@Override
public void remove() {
if (throwOnRemove) {
throw new UnsupportedOperationException();
}
base.remove();
}

/**
* Returns the next item and removes it from the iterator.
* @return the next item from the iterator.
*/
public T removeNext() {
T result = next();
remove();
return result;
}

/**
* Chains the {@code other} iterator to the end of this one.
*
* @param other the other iterator to extend this iterator with.
* @return A new iterator returning the contents of {@code this} iterator followed by the contents of {@code other} iterator.
* @param <X> The type of object returned from the other iterator.
Expand All @@ -159,38 +128,50 @@ public <X extends T> ExtendedIterator<T> andThen(final Iterator<X> other) {
((IteratorChain<T>) base).addIterator(other);
return this;
}
return new ExtendedIterator<T>(new IteratorChain<T>(this.base, other), this.throwOnRemove);
return new ExtendedIterator<>(new IteratorChain<>(this.base, other), this.throwOnRemove);
}

/**
* Filter this iterator using a predicate. Only items for which the predicate returns {@code true} will
* be included in the result.
* Filter this iterator using a predicate. Only items for which the predicate returns {@code true} will be included in the result.
*
* @param predicate The predicate to filter the items with.
* @return An iterator filtered by the predicate.
*/
public ExtendedIterator<T> filter(final Predicate<T> predicate) {
return new ExtendedIterator<T>(new FilterIterator<>(this, predicate::test), this.throwOnRemove);
return new ExtendedIterator<>(new FilterIterator<>(this, predicate::test), this.throwOnRemove);
}

@Override
public void forEachRemaining(final Consumer<? super T> action) {
base.forEachRemaining(action);
}

@Override
public boolean hasNext() {
return base.hasNext();
}

/**
* Map the elements of the iterator to a now type.
*
* @param function The function to map elements of {@code <T>} to type {@code <U>}.
* @return An Extended iterator that returns a {@code <U>} for very {@code <T>} in the original iterator.
* @param <U> The object type to return.
*/
public <U> ExtendedIterator<U> map(final Function<T, U> function) {
return new ExtendedIterator<U>(new TransformIterator<>(this, function::apply), false);
return new ExtendedIterator<>(new TransformIterator<>(this, function::apply), false);
}

/**
* A method to add the remaining elements in the iterator an arbitrary collection.
* This method consumes the iterator.
* @param collection THe collection to add elements to.
* @return the {@code collection} with the elements added.
* @param <U> A collection of objects of type {@code <T>}.
*/
public <U extends Collection<T>> U addTo(final U collection) {
this.forEachRemaining(collection::add);
return collection;
@Override
public T next() {
return base.next();
}

@Override
public void remove() {
if (throwOnRemove) {
throw new UnsupportedOperationException();
}
base.remove();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
* @param <E> the type of elements returned by this iterator.
* @since 1.0
*/
public class FilterIterator<E> implements Iterator<E> {
public class FilterIterator<E> implements IteratorOperations<E> {

/** The iterator to be filtered. */
private Iterator<? extends E> iterator;
Expand Down Expand Up @@ -141,18 +141,6 @@ public void remove() {
iterator.remove();
}

/**
* Returns the next item and removes it from the iterator.
*
* @return the next item from the iterator.
* @since 4.5.0-M3
*/
public E removeNext() {
final E result = next();
remove();
return result;
}

private Predicate<? super E> safePredicate(final Predicate<? super E> predicate) {
return predicate != null ? predicate : TruePredicate.truePredicate();
}
Expand Down
Loading

0 comments on commit d591c85

Please sign in to comment.