|
|
@@ -0,0 +1,397 @@
|
|
|
+/*
|
|
|
+ * @author Ranides Atterwim <ranides@gmail.com>
|
|
|
+ * @copyright Ranides Atterwim
|
|
|
+ * @license WTFPL
|
|
|
+ * @url http://ranides.net/projects/assira
|
|
|
+ */
|
|
|
+
|
|
|
+package net.ranides.assira.streams;
|
|
|
+
|
|
|
+import java.util.Arrays;
|
|
|
+import java.util.Collection;
|
|
|
+import java.util.Comparator;
|
|
|
+import java.util.Iterator;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.NoSuchElementException;
|
|
|
+import java.util.Set;
|
|
|
+import java.util.Spliterator;
|
|
|
+import java.util.function.BiPredicate;
|
|
|
+import java.util.function.BinaryOperator;
|
|
|
+import java.util.function.Consumer;
|
|
|
+import java.util.function.Function;
|
|
|
+import java.util.function.IntFunction;
|
|
|
+import java.util.function.IntSupplier;
|
|
|
+import java.util.function.Predicate;
|
|
|
+import java.util.function.Supplier;
|
|
|
+import java.util.stream.Collector;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+import java.util.stream.Stream;
|
|
|
+import java.util.stream.StreamSupport;
|
|
|
+import net.ranides.assira.collection.ACollection;
|
|
|
+import net.ranides.assira.collection.HashFunction;
|
|
|
+import net.ranides.assira.collection.arrays.ArraySort;
|
|
|
+import net.ranides.assira.collection.arrays.NativeArraySort;
|
|
|
+import net.ranides.assira.collection.iterators.IteratorUtils;
|
|
|
+import net.ranides.assira.collection.lists.JoinList;
|
|
|
+import net.ranides.assira.collection.lists.ListUtils;
|
|
|
+import net.ranides.assira.collection.sets.CustomSet;
|
|
|
+import net.ranides.assira.collection.sets.HashSet;
|
|
|
+import net.ranides.assira.collection.sets.RBTreeSet;
|
|
|
+import net.ranides.assira.functional.Fold;
|
|
|
+import net.ranides.assira.generic.LazyReference;
|
|
|
+
|
|
|
+/**
|
|
|
+ *
|
|
|
+ * @author Ranides Atterwim <ranides@gmail.com>
|
|
|
+ */
|
|
|
+public final class IStreams {
|
|
|
+
|
|
|
+ private IStreams() {
|
|
|
+ /* utility class */
|
|
|
+ }
|
|
|
+
|
|
|
+ public static <T> IStream<T> wrap(Iterable<T> source) {
|
|
|
+ return new SIterable<>(source);
|
|
|
+ }
|
|
|
+
|
|
|
+ public static <T> IStream<T> wrap(Collection<T> source) {
|
|
|
+ return new SCollection<>(source);
|
|
|
+ }
|
|
|
+
|
|
|
+ @SafeVarargs
|
|
|
+ public static <T> IStream<T> wrap(T... source) {
|
|
|
+ return new SCollection<>(Arrays.asList(source));
|
|
|
+ }
|
|
|
+
|
|
|
+ private static abstract class SAbstract<T> implements IStream<T> {
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public final <A, R> R collect(Collector<T, A, R> collector) {
|
|
|
+ return stream().collect(collector);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public final Object[] array() {
|
|
|
+ return stream().toArray();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public final <A> A[] array(IntFunction<A[]> generator) {
|
|
|
+ return stream().toArray(generator);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public final List<T> list() {
|
|
|
+ return stream().collect(Collectors.toList());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public final Set<T> set() {
|
|
|
+ return stream().collect(Collectors.toSet());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public final <K> Map<K,T> group(Function<T, K> key) {
|
|
|
+ return stream().collect(Collectors.toMap(key, v -> v));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public final <K, V> Map<K, V> group(Function<T, K> key, Function<T, V> value) {
|
|
|
+ return stream().collect(Collectors.toMap(key, value));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public final boolean isEmpty() {
|
|
|
+ return !iterator().hasNext();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public final IStream<T> filter(Predicate<T> p) {
|
|
|
+ return new SIterable<>(() -> IteratorUtils.filter(iterator(), p));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public final <R> IStream<R> map(Function<T, R> f) {
|
|
|
+ return new SIterable<>(() -> IteratorUtils.map(iterator(), f), this::size);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public final <R> IStream<R> unfold(Function<T, IStream<R>> f) {
|
|
|
+ return new SIterable<R>(() ->
|
|
|
+ IteratorUtils.flat(IteratorUtils.map(iterator(), v -> f.apply(v).iterator()))
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public final T fold(T identity, BinaryOperator<T> accumulator) {
|
|
|
+ Iterator<T> i = iterator();
|
|
|
+ return i.hasNext() ? Fold.apply(i, accumulator::apply) : identity;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public final T fold(BinaryOperator<T> accumulator) {
|
|
|
+ return fold(null, accumulator);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public final T min(Comparator<T> cmp) {
|
|
|
+ return fold((a,b) -> cmp.compare(a, b)<0 ? a : b);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public final T max(Comparator<T> cmp) {
|
|
|
+ return fold((a,b) -> cmp.compare(a, b)>0 ? a : b);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public final T first() {
|
|
|
+ return IteratorUtils.first(iterator());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public final T last() {
|
|
|
+ return IteratorUtils.last(iterator());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public final IStream<T> distinct() {
|
|
|
+ return new SSupplier<>(() -> new HashSet<>(iterator()));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public final IStream<T> distinct(HashFunction<T> eq) {
|
|
|
+ return new SSupplier<>(() -> new CustomSet<>(iterator(), eq));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public final IStream<T> distinct(Comparator<T> cmp) {
|
|
|
+ return new SSupplier<>(() -> new RBTreeSet<>(iterator(), cmp));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public final IStream<T> sort() {
|
|
|
+ return new SSupplier<>(() -> {
|
|
|
+ List<T> list = list();
|
|
|
+ ArraySort.mergeSort(list);
|
|
|
+ return list;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public final IStream<T> sort(Comparator<T> cmp) {
|
|
|
+ return new SSupplier<>(() -> {
|
|
|
+ List<T> list = list();
|
|
|
+ ArraySort.mergeSort(list, cmp);
|
|
|
+ return list;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public final void each(Consumer<T> consumer) {
|
|
|
+ stream().forEach(consumer);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public final boolean matchAny(Predicate<T> predicate) {
|
|
|
+ return stream().anyMatch(predicate);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public final boolean matchAll(Predicate<T> predicate) {
|
|
|
+ return stream().allMatch(predicate);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public final boolean matchNone(Predicate<T> predicate) {
|
|
|
+ return stream().noneMatch(predicate);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public IStream<T> limit(int n) {
|
|
|
+ return new SLimit<>(this, 0, n);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public IStream<T> skip(int n) {
|
|
|
+ return new SLimit<>(this, n, Integer.MAX_VALUE);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private static final class SCollection<T> extends SAbstract<T> {
|
|
|
+
|
|
|
+ private final Collection<T> source;
|
|
|
+
|
|
|
+
|
|
|
+ public SCollection(Collection<T> source) {
|
|
|
+ this.source = source;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Iterator<T> iterator() {
|
|
|
+ return source.iterator();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Spliterator<T> spliterator() {
|
|
|
+ return source.spliterator();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Stream<T> stream() {
|
|
|
+ return source.stream();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public int size() {
|
|
|
+ return source.size();
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private static final class SIterable<T> extends SAbstract<T> {
|
|
|
+
|
|
|
+ private final Iterable<T> source;
|
|
|
+
|
|
|
+ private final IntSupplier size;
|
|
|
+
|
|
|
+ public SIterable(Iterable<T> source) {
|
|
|
+ this.source = source;
|
|
|
+ this.size = () -> IteratorUtils.size(source.iterator());
|
|
|
+ }
|
|
|
+
|
|
|
+ public SIterable(Iterable<T> source, IntSupplier size) {
|
|
|
+ this.source = source;
|
|
|
+ this.size = size;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Iterator<T> iterator() {
|
|
|
+ return source.iterator();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Spliterator<T> spliterator() {
|
|
|
+ return source.spliterator();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Stream<T> stream() {
|
|
|
+ return StreamSupport.stream(spliterator(), false);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public int size() {
|
|
|
+ return size.getAsInt();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private static final class SSupplier<T> extends SAbstract<T> {
|
|
|
+
|
|
|
+ private final Supplier<Collection<T>> source;
|
|
|
+
|
|
|
+ public SSupplier(Supplier<Collection<T>> source) {
|
|
|
+ this.source = source;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Iterator<T> iterator() {
|
|
|
+ return source.get().iterator();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Spliterator<T> spliterator() {
|
|
|
+ return source.get().spliterator();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Stream<T> stream() {
|
|
|
+ return source.get().stream();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public int size() {
|
|
|
+ return source.get().size();
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private static final class SLimit<T> extends SAbstract<T> {
|
|
|
+
|
|
|
+ private final IStream<T> stream;
|
|
|
+ private final int begin;
|
|
|
+ private final int size;
|
|
|
+
|
|
|
+ public SLimit(IStream<T> stream, int begin, int size) {
|
|
|
+ this.stream = stream;
|
|
|
+ this.begin = begin;
|
|
|
+ this.size = size;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public IStream<T> limit(int n) {
|
|
|
+ return new SLimit<>(stream, begin, Math.min(n, size));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public IStream<T> skip(int n) {
|
|
|
+ return new SLimit<>(stream, begin+n, size);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Iterator<T> iterator() {
|
|
|
+ Iterator<T> i = stream.iterator();
|
|
|
+ IteratorUtils.next(i, begin);
|
|
|
+ return new LimitIterator<>(i, size);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Spliterator<T> spliterator() {
|
|
|
+ return stream().spliterator();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Stream<T> stream() {
|
|
|
+ return stream.stream().skip(begin).limit(size);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public int size() {
|
|
|
+ return Math.min(stream.size(), size);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private static final class LimitIterator<T> implements Iterator<T> {
|
|
|
+
|
|
|
+ private final Iterator<T> src;
|
|
|
+ private final int limit;
|
|
|
+ private int count;
|
|
|
+
|
|
|
+ public LimitIterator(Iterator<T> src, int limit) {
|
|
|
+ this.src = src;
|
|
|
+ this.limit = limit;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean hasNext() {
|
|
|
+ return count<limit && src.hasNext();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public T next() {
|
|
|
+ if(count>=limit) {
|
|
|
+ throw new NoSuchElementException();
|
|
|
+ }
|
|
|
+ count++;
|
|
|
+ return src.next();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+}
|