org.jooq.lambda.Seq Maven / Gradle / Ivy
Show all versions of jool Show documentation
/**
* Copyright (c) 2014, Data Geekery GmbH, [email protected]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jooq.lambda;
import static java.util.Comparator.comparing;
import static java.util.Comparator.naturalOrder;
import static java.util.Spliterator.ORDERED;
import static java.util.Spliterators.spliteratorUnknownSize;
import static org.jooq.lambda.tuple.Tuple.tuple;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.Spliterator;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.function.ToDoubleFunction;
import java.util.function.ToIntFunction;
import java.util.function.ToLongFunction;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import org.jooq.lambda.tuple.Tuple;
import org.jooq.lambda.tuple.Tuple2;
/**
* A sequential, ordered {@link Stream} that adds all sorts of useful methods that work only because
* it is sequential and ordered.
*
* @author Lukas Eder
*/
public interface Seq extends Stream, Iterable {
/**
* The underlying {@link Stream} implementation.
*/
Stream stream();
/**
* Concatenate two streams.
*
*
* // (1, 2, 3, 4, 5, 6)
* Seq.of(1, 2, 3).concat(Seq.of(4, 5, 6))
*
*
* @see #concat(Stream[])
*/
default Seq concat(Stream other) {
return Seq.concat(new Stream[]{this, other});
}
/**
* Repeat a stream infinitely.
*
*
* // (1, 2, 3, 1, 2, 3, ...)
* Seq.of(1, 2, 3).cycle();
*
*
* @see #cycle(Stream)
*/
default Seq cycle() {
return cycle(this);
}
/**
* Zip two streams into one.
*
*
* // (tuple(1, "a"), tuple(2, "b"), tuple(3, "c"))
* Seq.of(1, 2, 3).zip(Seq.of("a", "b", "c"))
*
*
* @see #zip(Stream, Stream)
*/
default Seq> zip(Seq other) {
return zip(this, other);
}
/**
* Zip two streams into one using a {@link BiFunction} to produce resulting values.
*
*
* // ("1:a", "2:b", "3:c")
* Seq.of(1, 2, 3).zip(Seq.of("a", "b", "c"), (i, s) -> i + ":" + s)
*
*
* @see #zip(Seq, BiFunction)
*/
default Seq zip(Seq other, BiFunction zipper) {
return zip(this, other, zipper);
}
/**
* Zip a Stream with a corresponding Stream of indexes.
*
*
* // (tuple("a", 0), tuple("b", 1), tuple("c", 2))
* Seq.of("a", "b", "c").zipWithIndex()
*
*
* @see #zipWithIndex(Stream)
*/
default Seq> zipWithIndex() {
return zipWithIndex(this);
}
/**
* Fold a Stream to the left.
*
*
* // "abc"
* Seq.of("a", "b", "c").foldLeft("", (u, t) -> u + t)
*
*/
default U foldLeft(U seed, BiFunction function) {
return foldLeft(this, seed, function);
}
/**
* Fold a Stream to the right.
*
*
* // "cba"
* Seq.of("a", "b", "c").foldRight("", (t, u) -> u + t)
*
*/
default U foldRight(U seed, BiFunction super T, U, U> function) {
return foldRight(this, seed, function);
}
/**
* Reverse a stream.
*
*
* // (3, 2, 1)
* Seq.of(1, 2, 3).reverse()
*
*/
default Seq reverse() {
return reverse(this);
}
/**
* Returns a stream with all elements skipped for which a predicate evaluates to true
.
*
*
* // (3, 4, 5)
* Seq.of(1, 2, 3, 4, 5).skipWhile(i -> i < 3)
*
*
* @see #skipWhile(Stream, Predicate)
*/
default Seq skipWhile(Predicate super T> predicate) {
return skipWhile(this, predicate);
}
/**
* Returns a stream with all elements skipped for which a predicate evaluates to false
.
*
*
* // (3, 4, 5)
* Seq.of(1, 2, 3, 4, 5).skipUntil(i -> i == 3)
*
*
* @see #skipUntil(Stream, Predicate)
*/
default Seq skipUntil(Predicate super T> predicate) {
return skipUntil(this, predicate);
}
/**
* Returns a stream limited to all elements for which a predicate evaluates to true
.
*
*
* // (1, 2)
* Seq.of(1, 2, 3, 4, 5).limitWhile(i -> i < 3)
*
*
* @see #limitWhile(Stream, Predicate)
*/
default Seq limitWhile(Predicate super T> predicate) {
return limitWhile(this, predicate);
}
/**
* Returns a stream limited to all elements for which a predicate evaluates to false
.
*
*
* // (1, 2)
* Seq.of(1, 2, 3, 4, 5).limitUntil(i -> i == 3)
*
*
* @see #limitUntil(Stream, Predicate)
*/
default Seq limitUntil(Predicate super T> predicate) {
return limitUntil(this, predicate);
}
/**
* Returns a stream with a given value interspersed between any two values of this stream.
*
*
* // (1, 0, 2, 0, 3, 0, 4)
* Seq.of(1, 2, 3, 4).intersperse(0)
*
*
* @see #intersperse(Stream, T)
*/
default Seq intersperse(T value) {
return intersperse(this, value);
}
/**
* Duplicate a Streams into two equivalent Streams.
*
*
* // tuple((1, 2, 3), (1, 2, 3))
* Seq.of(1, 2, 3).duplicate()
*
*
* @see #duplicate(Stream)
*/
default Tuple2, Seq> duplicate() {
return duplicate(this);
}
/**
* Partition a stream into two given a predicate.
*
*
* // tuple((1, 3, 5), (2, 4, 6))
* Seq.of(1, 2, 3, 4, 5, 6).partition(i -> i % 2 != 0)
*
*
* @see #partition(Stream, Predicate)
*/
default Tuple2, Seq> partition(Predicate super T> predicate) {
return partition(this, predicate);
}
/**
* Split a stream at a given position.
*
*
* // tuple((1, 2, 3), (4, 5, 6))
* Seq.of(1, 2, 3, 4, 5, 6).splitAt(3)
*
*
* @see #splitAt(Stream, long)
*/
default Tuple2, Seq> splitAt(long position) {
return splitAt(this, position);
}
/**
* Split a stream at the head.
*
*
* // tuple(1, (2, 3, 4, 5, 6))
* Seq.of(1, 2, 3, 4, 5, 6).splitHead(3)
*
*
* @see #splitAt(Stream, long)
*/
default Tuple2, Seq> splitAtHead() {
return splitAtHead(this);
}
/**
* Returns a limited interval from a given Stream.
*
*
* // (4, 5)
* Seq.of(1, 2, 3, 4, 5, 6).slice(3, 5)
*
*
* @see #slice(Stream, long, long)
*/
default Seq slice(long from, long to) {
return slice(this, from, to);
}
/**
* Collect a Stream into a Collection.
*
* @see #toCollection(Stream, Supplier)
*/
default > C toCollection(Supplier collectionFactory) {
return toCollection(this, collectionFactory);
}
/**
* Collect a Stream into a List.
*
* @see #toList(Stream)
*/
default List toList() {
return toList(this);
}
/**
* Collect a Stream into a Set.
*
* @see #toSet(Stream)
*/
default Set toSet() {
return toSet(this);
}
/**
* Collect a Stream into a Map.
*
* @see #toMap(Stream, Function, Function)
*/
default Map toMap(Function keyMapper, Function valueMapper) {
return toMap(this, keyMapper, valueMapper);
}
/**
* Consume a stream and concatenate all elements using a separator.
*/
default String toString(String separator) {
return toString(this, separator);
}
/**
* Get the maximum value by a function.
*/
default > Optional minBy(Function function) {
return minBy(function, naturalOrder());
}
/**
* Get the maximum value by a function.
*/
default Optional minBy(Function function, Comparator super U> comparator) {
return map(t -> tuple(t, function.apply(t)))
.min(comparing(Tuple2::v2, comparator))
.map(t -> t.v1);
}
/**
* Get the maximum value by a function.
*/
default > Optional maxBy(Function function) {
return maxBy(function, naturalOrder());
}
/**
* Get the maximum value by a function.
*/
default Optional maxBy(Function function, Comparator super U> comparator) {
return map(t -> tuple(t, function.apply(t)))
.max(comparing(Tuple2::v2, comparator))
.map(t -> t.v1);
}
// Methods taken from LINQ
// -----------------------
/**
* Keep only those elements in a stream that are of a given type.
*
*
* // (1, 2, 3)
* Seq.of(1, "a", 2, "b", 3).ofType(Integer.class)
*
*
* @see #ofType(Stream, Class)
*/
default Seq ofType(Class type) {
return ofType(this, type);
}
/**
* Cast all elements in a stream to a given type, possibly throwing a {@link ClassCastException}.
*
*
* // ClassCastException
* Seq.of(1, "a", 2, "b", 3).cast(Integer.class)
*
*
* @see #cast(Stream, Class)
*/
default Seq cast(Class type) {
return cast(this, type);
}
/**
* @see Stream#of(Object)
*/
static Seq of(T value) {
return seq(Stream.of(value));
}
/**
* @see Stream#of(Object[])
*/
static Seq of(T... values) {
return seq(Stream.of(values));
}
/**
* @see Stream#empty()
*/
static Seq empty() {
return seq(Stream.empty());
}
/**
* @see Stream#iterate(Object, UnaryOperator)
*/
static Seq iterate(final T seed, final UnaryOperator f) {
return seq(Stream.iterate(seed, f));
}
/**
* @see Stream#generate(Supplier)
*/
static Seq generate() {
return generate(() -> null);
}
/**
* @see Stream#generate(Supplier)
*/
static Seq generate(T value) {
return generate(() -> value);
}
/**
* @see Stream#generate(Supplier)
*/
static Seq generate(Supplier s) {
return seq(Stream.generate(s));
}
/**
* Wrap a Stream into a Seq.
*/
static Seq seq(Stream stream) {
if (stream instanceof Seq)
return (Seq) stream;
return new SeqImpl<>(stream);
}
/**
* Wrap an Iterable into a Seq.
*/
static Seq seq(Iterable iterable) {
return seq(iterable.iterator());
}
/**
* Wrap an Iterator into a Seq.
*/
static Seq seq(Iterator iterator) {
return seq(StreamSupport.stream(spliteratorUnknownSize(iterator, ORDERED), false));
}
/**
* Wrap a Map into a Seq.
*/
static Seq> seq(Map map) {
return seq(map.entrySet()).map(e -> tuple(e.getKey(), e.getValue()));
}
/**
* Repeat a stream infinitely.
*
*
* // (1, 2, 3, 1, 2, 3, ...)
* Seq.of(1, 2, 3).cycle();
*
*/
static Seq cycle(Stream stream) {
final List list = new ArrayList();
class Cycle implements Iterator {
boolean cycled;
Iterator it;
Cycle(Iterator it) {
this.it = it;
}
@Override
public boolean hasNext() {
return true;
}
@Override
public T next() {
if (!it.hasNext()) {
cycled = true;
it = list.iterator();
}
T next = it.next();
if (!cycled) {
list.add(next);
}
return next;
}
}
return seq(new Cycle(stream.iterator()));
}
/**
* Unzip one Stream into two.
*
*
* // tuple((1, 2, 3), (a, b, c))
* Seq.unzip(Seq.of(tuple(1, "a"), tuple(2, "b"), tuple(3, "c")));
*
*/
static Tuple2, Seq> unzip(Stream> stream) {
return unzip(stream, t -> t);
}
/**
* Unzip one Stream into two.
*
*
* // tuple((1, 2, 3), (a, b, c))
* Seq.unzip(Seq.of(tuple(1, "a"), tuple(2, "b"), tuple(3, "c")));
*
*/
static Tuple2, Seq> unzip(Stream> stream, Function leftUnzipper, Function rightUnzipper) {
return unzip(stream, t -> tuple(leftUnzipper.apply(t.v1), rightUnzipper.apply(t.v2)));
}
/**
* Unzip one Stream into two.
*
*
* // tuple((1, 2, 3), (a, b, c))
* Seq.unzip(Seq.of(tuple(1, "a"), tuple(2, "b"), tuple(3, "c")));
*
*/
static Tuple2, Seq> unzip(Stream> stream, Function, Tuple2> unzipper) {
return unzip(stream, (t1, t2) -> unzipper.apply(tuple(t1, t2)));
}
/**
* Unzip one Stream into two.
*
*
* // tuple((1, 2, 3), (a, b, c))
* Seq.unzip(Seq.of(tuple(1, "a"), tuple(2, "b"), tuple(3, "c")));
*
*/
static Tuple2, Seq> unzip(Stream> stream, BiFunction> unzipper) {
return seq(stream)
.map(t -> unzipper.apply(t.v1, t.v2))
.duplicate()
.map1(s -> s.map(u -> u.v1))
.map2(s -> s.map(u -> u.v2));
}
/**
* Zip two streams into one.
*
*
* // (tuple(1, "a"), tuple(2, "b"), tuple(3, "c"))
* Seq.of(1, 2, 3).zip(Seq.of("a", "b", "c"))
*
*/
static Seq> zip(Stream left, Stream right) {
return zip(left, right, Tuple::tuple);
}
/**
* Zip two streams into one using a {@link BiFunction} to produce resulting values.
*
*
* // ("1:a", "2:b", "3:c")
* Seq.of(1, 2, 3).zip(Seq.of("a", "b", "c"), (i, s) -> i + ":" + s)
*
*/
static Seq zip(Stream left, Stream right, BiFunction zipper) {
final Iterator it1 = left.iterator();
final Iterator it2 = right.iterator();
class Zip implements Iterator {
@Override
public boolean hasNext() {
return it1.hasNext() && it2.hasNext();
}
@Override
public R next() {
return zipper.apply(it1.next(), it2.next());
}
}
return seq(new Zip());
}
/**
* Zip a Stream with a corresponding Stream of indexes.
*
*
* // (tuple("a", 0), tuple("b", 1), tuple("c", 2))
* Seq.of("a", "b", "c").zipWithIndex()
*
*/
static Seq> zipWithIndex(Stream stream) {
final Iterator it = stream.iterator();
class ZipWithIndex implements Iterator> {
long index;
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
public Tuple2 next() {
return tuple(it.next(), index++);
}
}
return seq(new ZipWithIndex());
}
/**
* Fold a stream to the left.
*
*
* // "abc"
* Seq.of("a", "b", "c").foldLeft("", (u, t) -> u + t)
*
*/
static U foldLeft(Stream stream, U seed, BiFunction function) {
final Iterator it = stream.iterator();
U result = seed;
while (it.hasNext())
result = function.apply(result, it.next());
return result;
}
/**
* Fold a stream to the right.
*
*
* // "cba"
* Seq.of("a", "b", "c").foldRight("", (t, u) -> u + t)
*
*/
static U foldRight(Stream stream, U seed, BiFunction super T, U, U> function) {
return seq(stream).reverse().foldLeft(seed, (u, t) -> function.apply(t, u));
}
/**
* Unfold a function into a stream.
*
*
* // (1, 2, 3, 4, 5)
* Seq.unfold(1, i -> i <= 6 ? Optional.of(tuple(i, i + 1)) : Optional.empty())
*
*/
static Seq unfold(U seed, Function>> unfolder) {
class Unfold implements Iterator {
U u;
Optional> unfolded;
public Unfold(U u) {
this.u = u;
}
void unfold() {
if (unfolded == null)
unfolded = unfolder.apply(u);
}
@Override
public boolean hasNext() {
unfold();
return unfolded.isPresent();
}
@Override
public T next() {
unfold();
try {
return unfolded.get().v1;
}
finally {
u = unfolded.get().v2;
unfolded = null;
}
}
}
return seq(new Unfold(seed));
}
/**
* Reverse a stream.
*
*
* // (3, 2, 1)
* Seq.of(1, 2, 3).reverse()
*
*/
static Seq reverse(Stream stream) {
List list = toList(stream);
Collections.reverse(list);
return seq(list);
}
/**
* Concatenate a number of streams.
*
*
* // (1, 2, 3, 4, 5, 6)
* Seq.of(1, 2, 3).concat(Seq.of(4, 5, 6))
*
*/
@SafeVarargs
static Seq concat(Stream... streams) {
if (streams == null || streams.length == 0)
return Seq.empty();
if (streams.length == 1)
return seq(streams[0]);
Stream result = streams[0];
for (int i = 1; i < streams.length; i++)
result = Stream.concat(result, streams[i]);
return seq(result);
}
/**
* Duplicate a Streams into two equivalent Streams.
*
*
* // tuple((1, 2, 3), (1, 2, 3))
* Seq.of(1, 2, 3).duplicate()
*
*/
static Tuple2, Seq> duplicate(Stream stream) {
final LinkedList gap = new LinkedList<>();
final Iterator it = stream.iterator();
@SuppressWarnings("unchecked")
final Iterator[] ahead = new Iterator[] { null };
class Duplicate implements Iterator {
@Override
public boolean hasNext() {
if (ahead[0] == null || ahead[0] == this)
return it.hasNext();
return !gap.isEmpty();
}
@Override
public T next() {
if (ahead[0] == null)
ahead[0] = this;
if (ahead[0] == this) {
T value = it.next();
gap.offer(value);
return value;
}
return gap.poll();
}
}
return tuple(seq(new Duplicate()), seq(new Duplicate()));
}
/**
* Consume a stream and concatenate all elements.
*/
static String toString(Stream> stream) {
return toString(stream, "");
}
/**
* Consume a stream and concatenate all elements using a separator.
*/
static String toString(Stream> stream, String separator) {
return stream.map(Objects::toString).collect(Collectors.joining(separator));
}
/**
* Collect a Stream into a List.
*/
static > C toCollection(Stream stream, Supplier collectionFactory) {
return stream.collect(Collectors.toCollection(collectionFactory));
}
/**
* Collect a Stream into a List.
*/
static List toList(Stream stream) {
return stream.collect(Collectors.toList());
}
/**
* Collect a Stream into a Set.
*/
static Set toSet(Stream stream) {
return stream.collect(Collectors.toSet());
}
/**
* Collect a Stream into a Map.
*/
static Map toMap(Stream stream, Function keyMapper, Function valueMapper) {
return stream.collect(Collectors.toMap(keyMapper, valueMapper));
}
/**
* Returns a limited interval from a given Stream.
*
*
* // (4, 5)
* Seq.of(1, 2, 3, 4, 5, 6).slice(3, 5)
*
*/
static Seq slice(Stream stream, long from, long to) {
long f = Math.max(from, 0);
long t = Math.max(to - f, 0);
return seq(stream.skip(f).limit(t));
}
/**
* Returns a stream with n elements skipped.
*
*
* // (4, 5, 6)
* Seq.of(1, 2, 3, 4, 5, 6).skip(3)
*
*/
static Seq skip(Stream stream, long elements) {
return seq(stream.skip(elements));
}
/**
* Returns a stream with all elements skipped for which a predicate evaluates to true
.
*
*
* // (3, 4, 5)
* Seq.of(1, 2, 3, 4, 5).skipWhile(i -> i < 3)
*
*/
static Seq skipWhile(Stream stream, Predicate super T> predicate) {
return skipUntil(stream, predicate.negate());
}
/**
* Returns a stream with all elements skipped for which a predicate evaluates to false
.
*
*
* // (3, 4, 5)
* Seq.of(1, 2, 3, 4, 5).skipUntil(i -> i == 3)
*
*/
static Seq skipUntil(Stream stream, Predicate super T> predicate) {
final Iterator it = stream.iterator();
class SkipUntil implements Iterator {
T next = (T) SeqImpl.NULL;
boolean test = false;
void skip() {
while (next == SeqImpl.NULL && it.hasNext()) {
next = it.next();
if (test || (test = predicate.test(next)))
break;
else
next = (T) SeqImpl.NULL;
}
}
@Override
public boolean hasNext() {
skip();
return next != SeqImpl.NULL;
}
@Override
public T next() {
if (next == SeqImpl.NULL)
throw new NoSuchElementException();
try {
return next;
}
finally {
next = (T) SeqImpl.NULL;
}
}
}
return seq(new SkipUntil());
}
/**
* Returns a stream limited to n elements.
*
*
* // (1, 2, 3)
* Seq.of(1, 2, 3, 4, 5, 6).limit(3)
*
*/
static Seq limit(Stream stream, long elements) {
return seq(stream.limit(elements));
}
/**
* Returns a stream limited to all elements for which a predicate evaluates to true
.
*
*
* // (1, 2)
* Seq.of(1, 2, 3, 4, 5).limitWhile(i -> i < 3)
*
*/
static Seq limitWhile(Stream stream, Predicate super T> predicate) {
return limitUntil(stream, predicate.negate());
}
/**
* Returns a stream limited to all elements for which a predicate evaluates to true
.
*
*
* // (1, 2)
* Seq.of(1, 2, 3, 4, 5).limitUntil(i -> i == 3)
*
*/
static Seq limitUntil(Stream stream, Predicate super T> predicate) {
final Iterator it = stream.iterator();
class LimitUntil implements Iterator {
T next = (T) SeqImpl.NULL;
boolean test = false;
void test() {
if (!test && next == SeqImpl.NULL && it.hasNext()) {
next = it.next();
if (test = predicate.test(next))
next = (T) SeqImpl.NULL;
}
}
@Override
public boolean hasNext() {
test();
return next != SeqImpl.NULL;
}
@Override
public T next() {
if (next == SeqImpl.NULL)
throw new NoSuchElementException();
try {
return next;
}
finally {
next = (T) SeqImpl.NULL;
}
}
}
return seq(new LimitUntil());
}
/**
* Returns a stream with a given value interspersed between any two values of this stream.
*
*
* // (1, 0, 2, 0, 3, 0, 4)
* Seq.of(1, 2, 3, 4).intersperse(0)
*
*/
static Seq intersperse(Stream stream, T value) {
return seq(stream.flatMap(t -> Stream.of(value, t)).skip(1));
}
/**
* Partition a stream into two given a predicate.
*
*
* // tuple((1, 3, 5), (2, 4, 6))
* Seq.of(1, 2, 3, 4, 5, 6).partition(i -> i % 2 != 0)
*
*/
static Tuple2, Seq> partition(Stream stream, Predicate super T> predicate) {
final Iterator it = stream.iterator();
final LinkedList buffer1 = new LinkedList<>();
final LinkedList buffer2 = new LinkedList<>();
class Partition implements Iterator {
final boolean b;
Partition(boolean b) {
this.b = b;
}
void fetch() {
while (buffer(b).isEmpty() && it.hasNext()) {
T next = it.next();
buffer(predicate.test(next)).offer(next);
}
}
LinkedList buffer(boolean test) {
return test ? buffer1 : buffer2;
}
@Override
public boolean hasNext() {
fetch();
return !buffer(b).isEmpty();
}
@Override
public T next() {
return buffer(b).poll();
}
}
return tuple(seq(new Partition(true)), seq(new Partition(false)));
}
/**
* Split a stream at a given position.
*
*
* // tuple((1, 2, 3), (4, 5, 6))
* Seq.of(1, 2, 3, 4, 5, 6).splitAt(3)
*
*/
static Tuple2, Seq> splitAt(Stream stream, long position) {
return seq(stream)
.zipWithIndex()
.partition(t -> t.v2 < position)
.map((v1, v2) -> tuple(
v1.map(t -> t.v1),
v2.map(t -> t.v1)
));
}
/**
* Split a stream at the head.
*