244 lines
8.6 KiB
Java
244 lines
8.6 KiB
Java
package tc.oc.commons.core.util;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Collection;
|
|
import java.util.Collections;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.Objects;
|
|
import java.util.Optional;
|
|
import java.util.Random;
|
|
import java.util.function.BiFunction;
|
|
import java.util.Spliterator;
|
|
import java.util.Spliterators;
|
|
import java.util.function.Consumer;
|
|
import java.util.function.IntSupplier;
|
|
import java.util.function.Predicate;
|
|
import java.util.function.Supplier;
|
|
import java.util.stream.Collectors;
|
|
import java.util.stream.Stream;
|
|
import java.util.stream.StreamSupport;
|
|
import javax.annotation.Nullable;
|
|
|
|
import com.google.common.collect.Lists;
|
|
import tc.oc.commons.core.IterableUtils;
|
|
|
|
public final class Streams {
|
|
private Streams() {}
|
|
|
|
private static final Consumer NO_OP = o -> {};
|
|
public static void consume(Stream<?> stream) {
|
|
stream.forEach(NO_OP);
|
|
}
|
|
|
|
public static <T> Stream<T> reverseStream(Iterable<T> iterable) {
|
|
return Lists.reverse(Lists.newArrayList(iterable)).stream();
|
|
}
|
|
|
|
public static <T, E extends Throwable> void forEachThrows(Stream<T> stream, ThrowingConsumer<? super T, E> consumer) throws E {
|
|
for(final Iterator<T> it = stream.iterator(); it.hasNext();) {
|
|
consumer.acceptThrows(it.next());
|
|
}
|
|
}
|
|
|
|
public static <T> void forEachWithIndex(Stream<T> stream, IndexedConsumer<T> consumer) {
|
|
final Counter index = new Counter();
|
|
stream.forEachOrdered(t -> consumer.accept(t, index.next()));
|
|
}
|
|
|
|
public static <T> Stream<T> reverse(Stream<T> stream) {
|
|
return Lists.reverse(stream.collect(Collectors.toCollection(ArrayList::new))).stream();
|
|
}
|
|
|
|
public static <T> void reverseForEach(Stream<T> stream, Consumer<? super T> consumer) {
|
|
reverse(stream).forEach(consumer);
|
|
}
|
|
|
|
public static <T, E extends Throwable> void reverseForEachThrows(Stream<T> stream, ThrowingConsumer<? super T, E> consumer) throws E {
|
|
forEachThrows(reverse(stream), consumer);
|
|
}
|
|
|
|
public static <T> Stream<T> shuffle(Stream<T> stream, Random random) {
|
|
final List<T> list = stream.collect(Collectors.toCollection(ArrayList::new));
|
|
Collections.shuffle(list, random);
|
|
return list.stream();
|
|
}
|
|
|
|
public static <T> Stream<T> shuffle(Stream<T> stream) {
|
|
final List<T> list = stream.collect(Collectors.toCollection(ArrayList::new));
|
|
Collections.shuffle(list);
|
|
return list.stream();
|
|
}
|
|
|
|
public static <T> Stream<T> flatten(Stream<Stream<? extends T>> streams) {
|
|
return (Stream<T>) streams.reduce(Stream::concat)
|
|
.orElse(Stream.of());
|
|
}
|
|
|
|
public static <T> Stream<T> concat(Stream<? extends T>... streams) {
|
|
return flatten(Stream.of(streams));
|
|
}
|
|
|
|
public static <T> Stream<T> instancesOf(Stream<?> stream, Class<T> type) {
|
|
return (Stream<T>) stream.filter(type::isInstance);
|
|
}
|
|
|
|
public static <T> Stream<Class<? extends T>> subtypesOf(Stream<? extends Class<?>> stream, Class<T> type) {
|
|
return (Stream) stream.filter(type::isAssignableFrom);
|
|
}
|
|
|
|
public static <T> boolean any(Stream<T> stream, Predicate<? super T> predicate) {
|
|
return stream.filter(predicate).findAny().isPresent();
|
|
}
|
|
|
|
public static <T> boolean all(Stream<T> stream, Predicate<? super T> predicate) {
|
|
return any(stream, predicate.negate());
|
|
}
|
|
|
|
public static <T> boolean none(Stream<T> stream, Predicate<? super T> predicate) {
|
|
return !any(stream, predicate);
|
|
}
|
|
|
|
public static <T> Stream<T> copyOf(Stream<T> stream) {
|
|
return stream.collect(Collectors.toList()).stream();
|
|
}
|
|
|
|
public static <T> Stream<T> copyOf(Collection<T> collection) {
|
|
return IterableUtils.copyOf(collection).stream();
|
|
}
|
|
|
|
public static <T> Stream<T> append(Stream<T> stream, T... elements) {
|
|
return Stream.concat(stream, Stream.of(elements));
|
|
}
|
|
|
|
public static <T> Stream<T> prepend(Stream<T> stream, T... elements) {
|
|
return Stream.concat(Stream.of(elements), stream);
|
|
}
|
|
|
|
public static <T> Stream<T> remove(Stream<T> stream, T... elements) {
|
|
return stream.filter(t -> !ArrayUtils.contains(elements, t));
|
|
}
|
|
|
|
public static <T> Stream<T> of(Iterator<T> iterator) {
|
|
return StreamSupport.stream(Spliterators.spliterator(iterator, 0, Spliterator.ORDERED), false);
|
|
}
|
|
|
|
public static <T> Stream<T> of(Iterable<T> iterable) {
|
|
if(iterable instanceof Collection) {
|
|
return ((Collection<T>) iterable).stream();
|
|
} else {
|
|
return StreamSupport.stream(iterable.spliterator(), false);
|
|
}
|
|
}
|
|
|
|
public static <T> Stream<T> ofNullable(@Nullable T t) {
|
|
return t == null ? Stream.empty() : Stream.of(t);
|
|
}
|
|
|
|
public static <T> Stream<T> ofNullables(Stream<T> s) {
|
|
return s.filter(t -> t != null);
|
|
}
|
|
|
|
public static <T> Stream<T> ofNullables(T... things) {
|
|
return ofNullables(Stream.of(things));
|
|
}
|
|
|
|
public static <T> Stream<T> conditional(boolean condition, Stream<T> stream) {
|
|
return condition ? stream : Stream.empty();
|
|
}
|
|
|
|
public static <T> Stream<T> conditional(boolean condition, T element) {
|
|
return condition ? Stream.of(element) : Stream.empty();
|
|
}
|
|
|
|
public static <T> Stream<T> conditional(boolean condition, Supplier<? extends T> element) {
|
|
return condition ? Stream.of(element.get()) : Stream.empty();
|
|
}
|
|
|
|
public static <T> Stream<T> compact(Stream<Optional<T>> stream) {
|
|
return stream.filter(Optional::isPresent).map(Optional::get);
|
|
}
|
|
|
|
public static <T> Stream<T> compact(Optional<T>... elements) {
|
|
return compact(Stream.of(elements));
|
|
}
|
|
|
|
public static <T> Stream<T> compact1(T t1, Optional<T>... elements) {
|
|
return Stream.concat(Stream.of(t1), compact(Stream.of(elements)));
|
|
}
|
|
|
|
public static <T> Stream<T> compact2(T t1, T t2, Optional<T>... elements) {
|
|
return Stream.concat(Stream.of(t1, t2), compact(Stream.of(elements)));
|
|
}
|
|
|
|
public static <T> Stream<T> compact3(T t1, T t2, T t3, Optional<T>... elements) {
|
|
return Stream.concat(Stream.of(t1, t2, t3), compact(Stream.of(elements)));
|
|
}
|
|
|
|
public static <T, R> R reduce(Stream<T> stream, R identity, BiFunction<R, T, R> accumulator) {
|
|
class Result { R v; }
|
|
Result result = new Result();
|
|
result.v = identity;
|
|
stream.forEachOrdered(t -> result.v = accumulator.apply(result.v, t));
|
|
return result.v;
|
|
}
|
|
|
|
/**
|
|
* Test if all elements of the stream are equal to each other, according to {@link Objects#equals(Object, Object)}.
|
|
* Returns true for an empty stream, or a stream of all nulls.
|
|
*/
|
|
public static <T> boolean isUniform(Stream<T> stream) {
|
|
return stream.reduce(Uniformity.EMPTY, Uniformity::add, Uniformity::combine).isUniform();
|
|
}
|
|
|
|
/**
|
|
* This is surprisingly complex, but it is the simplest approach I could come up
|
|
* with that is actually in the spirit of streams. This is actually quite efficient,
|
|
* creating at most one temporary object per reduction thread.
|
|
*/
|
|
private interface Uniformity<T> {
|
|
boolean isUniform();
|
|
Uniformity<T> add(T t);
|
|
Uniformity<T> combine(Uniformity<T> that);
|
|
|
|
// Initial (empty) result
|
|
Uniformity EMPTY = new Uniformity() {
|
|
@Override public boolean isUniform() { return true; }
|
|
@Override public Uniformity add(Object t) { return new Intermediate(t); }
|
|
@Override public Uniformity combine(Uniformity that) { return that; }
|
|
};
|
|
|
|
// Result after multiple distinct values have been seen
|
|
Uniformity FAILED = new Uniformity() {
|
|
@Override public boolean isUniform() { return false; }
|
|
@Override public Uniformity add(Object t) { return this; }
|
|
@Override public Uniformity combine(Uniformity that) { return this; }
|
|
};
|
|
|
|
// Result after one or more values have been seen, and they are all equal
|
|
class Intermediate<T> implements Uniformity<T> {
|
|
final T value;
|
|
|
|
public Intermediate(T value) {
|
|
this.value = value;
|
|
}
|
|
|
|
@Override public boolean isUniform() {
|
|
return true;
|
|
}
|
|
|
|
@Override public Uniformity<T> add(T t) {
|
|
return Objects.equals(value, t) ? this : FAILED;
|
|
}
|
|
|
|
@Override public Uniformity<T> combine(Uniformity<T> that) {
|
|
if(that instanceof Intermediate) {
|
|
return add(((Intermediate<T>) that).value);
|
|
} else {
|
|
return that.combine(this);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|