|
|
@@ -0,0 +1,415 @@
|
|
|
+/*
|
|
|
+ * @author Ranides Atterwim <ranides@gmail.com>
|
|
|
+ * @copyright Ranides Atterwim
|
|
|
+ * @license WTFPL
|
|
|
+ * @url http://ranides.net/projects/assira
|
|
|
+ */
|
|
|
+package net.ranides.assira.collection;
|
|
|
+
|
|
|
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
|
|
+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.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.arrays.ArraySort;
|
|
|
+import net.ranides.assira.collection.arrays.ArrayUtils;
|
|
|
+import net.ranides.assira.collection.iterators.IteratorUtils;
|
|
|
+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;
|
|
|
+
|
|
|
+/**
|
|
|
+ *
|
|
|
+ * @author Ranides Atterwim <ranides@gmail.com>
|
|
|
+ */
|
|
|
+abstract class ACollectionQuery<T> implements CollectionQuery<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 CollectionQuery<T> limit(int n) {
|
|
|
+ return new CQBounded<>(this, 0, n);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public CollectionQuery<T> skip(int n) {
|
|
|
+ return new CQBounded<>(this, n, Integer.MAX_VALUE);
|
|
|
+ }
|
|
|
+
|
|
|
+ @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 CollectionQuery<T> filter(Predicate<T> p) {
|
|
|
+ return new CQIterable<>(() -> IteratorUtils.filter(iterator(), p));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public final <R> CollectionQuery<R> map(Function<T, R> f) {
|
|
|
+ return new CQIterable<>(() -> IteratorUtils.map(iterator(), f), this::size);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public final <R> CollectionQuery<R> unfold(Function<T, CollectionQuery<R>> f) {
|
|
|
+ return new CQIterable<>(() -> 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 CollectionQuery<T> distinct() {
|
|
|
+ return new CQLazy<>(() -> new HashSet<>(iterator()));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public final CollectionQuery<T> distinct(HashFunction<T> eq) {
|
|
|
+ return new CQLazy<>(() -> new CustomSet<>(iterator(), eq));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public final CollectionQuery<T> distinct(Comparator<T> cmp) {
|
|
|
+ return new CQLazy<>(() -> new RBTreeSet<>(iterator(), cmp));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public final CollectionQuery<T> sort() {
|
|
|
+ return new CQLazy<>(() -> {
|
|
|
+ List<T> list = list();
|
|
|
+ ArraySort.mergeSort(list);
|
|
|
+ return list;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public final CollectionQuery<T> sort(Comparator<T> cmp) {
|
|
|
+ return new CQLazy<>(() -> {
|
|
|
+ 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);
|
|
|
+ }
|
|
|
+
|
|
|
+ protected static final class CQCollection<T> extends ACollectionQuery<T> {
|
|
|
+
|
|
|
+ private final Collection<T> source;
|
|
|
+
|
|
|
+
|
|
|
+ public CQCollection(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();
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ protected static final class CQArray<T> extends ACollectionQuery<T> {
|
|
|
+
|
|
|
+ private final T[] source;
|
|
|
+ private final int offset;
|
|
|
+ private final int limit;
|
|
|
+
|
|
|
+ public CQArray(T[] source) {
|
|
|
+ this(source, 0, source.length);
|
|
|
+ }
|
|
|
+
|
|
|
+ @SuppressWarnings("PMD.ArrayIsStoredDirectly")
|
|
|
+ @SuppressFBWarnings("EI_EXPOSE_REP2")
|
|
|
+ public CQArray(T[] source, int offset, int limit) {
|
|
|
+ this.source = source;
|
|
|
+ this.offset = offset;
|
|
|
+ this.limit = Math.min(source.length, limit);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Iterator<T> iterator() {
|
|
|
+ return ArrayUtils.iterator(source, offset, offset+limit);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Spliterator<T> spliterator() {
|
|
|
+ return Arrays.spliterator(source, offset, offset+limit);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Stream<T> stream() {
|
|
|
+ return Arrays.stream(source, offset, offset+limit);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public int size() {
|
|
|
+ return limit;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public CollectionQuery<T> limit(int n) {
|
|
|
+ return new CQArray<>(source, offset, Math.min(limit, n));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public CollectionQuery<T> skip(int n) {
|
|
|
+ return new CQArray<>(source, offset+n, limit);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ protected static final class CQIterable<T> extends ACollectionQuery<T> {
|
|
|
+
|
|
|
+ private final Iterable<T> source;
|
|
|
+
|
|
|
+ private final IntSupplier size;
|
|
|
+
|
|
|
+ public CQIterable(Iterable<T> source) {
|
|
|
+ this.source = source;
|
|
|
+ this.size = () -> IteratorUtils.size(source.iterator());
|
|
|
+ }
|
|
|
+
|
|
|
+ public CQIterable(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();
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ protected static final class CQLazy<T> extends ACollectionQuery<T> {
|
|
|
+
|
|
|
+ private final Supplier<Collection<T>> source;
|
|
|
+
|
|
|
+ public CQLazy(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();
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ protected static final class CQBounded<T> extends ACollectionQuery<T> {
|
|
|
+
|
|
|
+ private final CollectionQuery<T> stream;
|
|
|
+ private final int begin;
|
|
|
+ private final int size;
|
|
|
+
|
|
|
+ public CQBounded(CollectionQuery<T> stream, int begin, int size) {
|
|
|
+ this.stream = stream;
|
|
|
+ this.begin = begin;
|
|
|
+ this.size = size;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public CollectionQuery<T> limit(int n) {
|
|
|
+ return new CQBounded<>(stream, begin, Math.min(n, size));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public CollectionQuery<T> skip(int n) {
|
|
|
+ return new CQBounded<>(stream, begin+n, size);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Iterator<T> iterator() {
|
|
|
+ Iterator<T> i = stream.iterator();
|
|
|
+ IteratorUtils.next(i, begin);
|
|
|
+ return new BIterator<>(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 BIterator<T> implements Iterator<T> {
|
|
|
+
|
|
|
+ private final Iterator<T> src;
|
|
|
+ private final int limit;
|
|
|
+ private int count;
|
|
|
+
|
|
|
+ public BIterator(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();
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+}
|