
de.unkrig.commons.util.collections.IteratorUtil Maven / Gradle / Ivy
/*
* de.unkrig.commons - A general-purpose Java class library
*
* Copyright (c) 2015, Arno Unkrig
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
* following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the
* following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
* products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package de.unkrig.commons.util.collections;
import java.util.Collection;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.NoSuchElementException;
import de.unkrig.commons.lang.protocol.PredicateWhichThrows;
import de.unkrig.commons.lang.protocol.Transformer;
import de.unkrig.commons.nullanalysis.NotNull;
import de.unkrig.commons.nullanalysis.NotNullByDefault;
import de.unkrig.commons.nullanalysis.Nullable;
/**
* Various {@link Iterator}-related utility methods.
*/
public final
class IteratorUtil {
private
IteratorUtil() {}
/**
* An iterator which is at its end.
*
* To get the non-rawe type, use {@link #atEnd()}.
*
*/
@SuppressWarnings("rawtypes") public static final Iterator
AT_END = new Iterator() {
@Override public boolean hasNext() { return false; }
@Override public Object next() { throw new NoSuchElementException(); }
@Override public void remove() { throw new UnsupportedOperationException(); }
};
/**
* @return An {@link Iterator} which is always at its end
*/
@SuppressWarnings("unchecked") public static Iterator
atEnd() { return IteratorUtil.AT_END; }
/**
* Returns an iterator which skips the elements of the delegate which do not qualifiy.
*/
public static Iterator
filter(
final Iterator extends T> delegate,
final PredicateWhichThrows super T, ? extends RuntimeException> qualifies
) {
return new Iterator() {
@Nullable T lookahead;
@Override public boolean
hasNext() {
if (this.lookahead != null) return true;
for (;;) {
if (!delegate.hasNext()) return false;
T tmp = delegate.next();
if (qualifies.evaluate(tmp)) {
this.lookahead = tmp;
return true;
}
}
}
@Override public T
next() {
if (this.lookahead != null) {
T tmp = this.lookahead;
this.lookahead = null;
return tmp;
}
for (;;) {
T tmp = delegate.next();
if (qualifies.evaluate(tmp)) return tmp;
}
}
@Override public void
remove() { delegate.remove(); }
};
}
/**
* Returns an iterator which iterates the transformed elements of the delegate
*/
public static Iterator
transform(final Iterator extends I> delegate, final Transformer super I, ? extends O> transform) {
return new Iterator() {
@Override public boolean hasNext() { return delegate.hasNext(); }
@Override public O next() { return transform.transform(delegate.next()); }
@Override public void remove() { delegate.remove(); }
};
}
/**
* @return An iterator that produces an infinite sequence of value elements
*/
public static Iterator
repeat(final T value) {
return new Iterator() {
@Override public boolean hasNext() { return true; }
@Override public T next() { return value; }
@Override public void remove() { throw new UnsupportedOperationException(); }
};
}
/**
* Returns a list iterator that traverses the list elements in the reverse order of the given delegate.
*
* Notice that iff {@code !}delegate{@code .hasPrevious()}, then {@code
* !reverse(}delegate{@code ).hasNext()}. In other words, you will often use
*
* {@code IteratorUtil.reverse(list.listIterator(list.size()))}
*
* , and not simply
*
* {@code IteratorUtil.reverse(list.listIterator())}
*/
public static ListIterator
reverse(final ListIterator delegate) {
return new ListIterator() {
@Override public boolean hasNext() { return delegate.hasPrevious(); }
@Override public E next() { return delegate.previous(); }
@Override public boolean hasPrevious() { return delegate.hasNext(); }
@Override public E previous() { return delegate.next(); }
@Override @NotNullByDefault(false) public void set(E e) { delegate.set(e); }
@Override @NotNullByDefault(false) public void add(E e) { delegate.set(e); }
@Override public void remove() { delegate.remove(); }
@Override public int nextIndex() { throw new UnsupportedOperationException("nextIndex"); }
@Override public int previousIndex() { throw new UnsupportedOperationException("previousIndex"); }
};
}
/**
* @return An iterator that produces a sequence of n value elements
*/
public static Iterator
repeat(final int n, final T value) {
return new Iterator() {
int count;
@Override public boolean
hasNext() { return this.count < n; }
@Override public T
next() {
if (this.count >= n) throw new NoSuchElementException();
this.count++;
return value;
}
@Override public void
remove() { throw new UnsupportedOperationException(); }
};
}
/**
* An {@link Iterator} that has a notion of an "array index", which is the index of the "next" element in an
* array.
*
* @param See {@link Iterator}
*/
public
interface ArrayIterator extends Iterator {
/**
* @return The index of the "next" element in the array; array{@code .length} on
* end-of-array
*/
int index();
}
/**
* @return An iterator over the elements of the array
*/
public static ArrayIterator
iterator(final T[] array) {
return new ArrayIterator() {
int index;
@Override public boolean
hasNext() { return this.index < array.length; }
@Override public T
next() {
if (this.index >= array.length) throw new NoSuchElementException();
return array[this.index++];
}
@Override public void
remove() { throw new UnsupportedOperationException("remove"); }
@Override public int
index() { return this.index; }
};
}
/**
* Retrieves, counts and discards all elements remaining on the iterator
*/
public static int
elementCount(Iterator iterator) {
int n = 0;
for (; iterator.hasNext(); iterator.next()) n++;
return n;
}
/**
* Wraps the delegate iterator in an {@code Iterator}.
*
* @param delegate Must produce non-{@code null} values
* @return An iterator that produces {@link ElementWithContext}s from the sequence produced by the
* delegate
*/
public static Iterator>
iteratorWithContext(final Iterator extends T> delegate) {
class ElementWithContextIterator implements Iterator> {
@Nullable private T current, next;
@Override public boolean
hasNext() {
return this.next != null || delegate.hasNext();
}
@Override public ElementWithContext
next() {
final T previous = this.current;
if (this.next == null) {
T e = delegate.next();
assert e != null : "Delegate must produce non-null values";
this.current = e;
} else {
this.current = this.next;
this.next = null;
}
return new ElementWithContext() {
@Override @Nullable public T
previous() { return previous; }
@Override @NotNull public T
current() {
T result = ElementWithContextIterator.this.current;
assert result != null;
return result;
}
@Override @Nullable public T
next() {
if (ElementWithContextIterator.this.next != null) return ElementWithContextIterator.this.next;
if (!delegate.hasNext()) return null;
T e = delegate.next();
assert e != null : "Delegate must produce non-null values";
ElementWithContextIterator.this.next = e;
return ElementWithContextIterator.this.next;
}
};
}
@Override public void
remove() { throw new UnsupportedOperationException("remove"); }
}
return new ElementWithContextIterator();
}
/**
* Equivalent with {@link #foR(int, int, int) foR}{@code (}start{@code ,} end{@code ,1)}.
*
* @see #foR(int, int, int)
*/
public static Iterator
foR(final int start, final int end) { return IteratorUtil.foR(start, end, 1); }
/**
* Creates and returns an {@link Iterator Iterator<Integer>} that counts from start (inclusively) to
* end (exclusively), with a step increment.
*
* More precise: Iff step is greater than zero, then the returned iterator produces the values
* start, start {@code +} step, and so forth, and ends with the last value which
* is less than end.
*
*
* Otherwise, iff step is less than zero, then the returned iterator produces the values
* start, start {@code +} step, and so forth, and ends with the last value which
* is greater than end.
*
*
* Otherwise, step is zero, and the returned iterator produces either an
* infinite sequence of values start, or, iff start {@code == } end, an empty
* sequence.
*
*
* @throws IllegalArgumentException step {@code > 0 &&} end {@code <} start
* @throws IllegalArgumentException step {@code < 0 &&} end {@code >} start
*/
public static Iterator
foR(final int start, final int end, final int step) {
if (step == 0) {
// Optimize for the "step == 0" case.
return start == end ? IteratorUtil.atEnd() : IteratorUtil.repeat(start);
} else
if (step > 0) {
if (end < start) throw new IllegalArgumentException("step > 0 and end < start");
return new Iterator() {
int nextValue = start;
@Override public boolean
hasNext() {
return this.nextValue < end;
}
@Override public Integer
next() {
if (this.nextValue >= end) throw new NoSuchElementException();
int result = this.nextValue;
this.nextValue += step;
return result;
}
@Override public void
remove() {
throw new UnsupportedOperationException("remove");
}
};
} else
{
if (end > start) throw new IllegalArgumentException("step < 0 and end > start");
return new Iterator() {
int nextValue = start;
@Override public boolean
hasNext() {
return this.nextValue > end;
}
@Override public Integer
next() {
if (this.nextValue <= end) throw new NoSuchElementException();
int result = this.nextValue;
this.nextValue += step;
return result;
}
@Override public void
remove() {
throw new UnsupportedOperationException("remove");
}
};
}
}
/**
* Adds all remaining products of the iterator to the target collection.
*/
public static void
addAllElementsTo(Iterator extends T> iterator, Collection super T> target) {
while (iterator.hasNext()) target.add(iterator.next());
}
}