ProjectAres/Util/core/src/main/java/tc/oc/commons/core/util/Streams.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);
}
}
}
}
}