All Downloads are FREE. Search and download functionalities are using the official Maven repository.

javaslang.collection.Stream Maven / Gradle / Ivy

There is a newer version: 2.1.0-alpha
Show newest version
/*     / \____  _    _  ____   ______  / \ ____  __    _______
 *    /  /    \/ \  / \/    \ /  /\__\/  //    \/  \  //  /\__\   JΛVΛSLΛNG
 *  _/  /  /\  \  \/  /  /\  \\__\\  \  //  /\  \ /\\/ \ /__\ \   Copyright 2014-2016 Javaslang, http://javaslang.io
 * /___/\_/  \_/\____/\_/  \_/\__\/__/\__\_/  \_//  \__/\_____/   Licensed under the Apache License, Version 2.0
 */
package javaslang.collection;

import javaslang.*;
import javaslang.collection.Stream.Cons;
import javaslang.collection.Stream.Empty;
import javaslang.collection.StreamModule.*;
import javaslang.control.Option;

import java.io.*;
import java.util.*;
import java.util.function.*;
import java.util.stream.Collector;

/**
 * An immutable {@code Stream} is lazy sequence of elements which may be infinitely long.
 * Its immutability makes it suitable for concurrent programming.
 * 

* A {@code Stream} is composed of a {@code head} element and a lazy evaluated {@code tail} {@code Stream}. *

* There are two implementations of the {@code Stream} interface: *

    *
  • {@link Empty}, which represents the empty {@code Stream}.
  • *
  • {@link Cons}, which represents a {@code Stream} containing one or more elements.
  • *
* Methods to obtain a {@code Stream}: *
 * 
 * // factory methods
 * Stream.empty()                  // = Stream.of() = Nil.instance()
 * Stream.of(x)                    // = new Cons<>(x, Nil.instance())
 * Stream.of(Object...)            // e.g. Stream.of(1, 2, 3)
 * Stream.ofAll(Iterable)          // e.g. Stream.ofAll(List.of(1, 2, 3)) = 1, 2, 3
 * Stream.ofAll(<primitive array>) // e.g. List.ofAll(new int[] {1, 2, 3}) = 1, 2, 3
 *
 * // int sequences
 * Stream.from(0)                  // = 0, 1, 2, 3, ...
 * Stream.range(0, 3)              // = 0, 1, 2
 * Stream.rangeClosed(0, 3)        // = 0, 1, 2, 3
 *
 * // generators
 * Stream.cons(Object, Supplier)   // e.g. Stream.cons(current, () -> next(current));
 * Stream.continually(Supplier)    // e.g. Stream.continually(Math::random);
 * Stream.iterate(Object, Function)// e.g. Stream.iterate(1, i -> i * 2);
 * 
 * 
* * Factory method applications: * *
 * 
 * Stream<Integer>       s1 = Stream.of(1);
 * Stream<Integer>       s2 = Stream.of(1, 2, 3);
 *                       // = Stream.of(new Integer[] {1, 2, 3});
 *
 * Stream<int[]>         s3 = Stream.ofAll(new int[] {1, 2, 3});
 * Stream<List<Integer>> s4 = Stream.ofAll(List.of(1, 2, 3));
 *
 * Stream<Integer>       s5 = Stream.ofAll(new int[] {1, 2, 3});
 * Stream<Integer>       s6 = Stream.ofAll(List.of(1, 2, 3));
 *
 * // cuckoo's egg
 * Stream<Integer[]>     s7 = Stream.<Integer[]> of(new Integer[] {1, 2, 3});
 * 
 * 
* * Example: Generating prime numbers * *
 * 
 * // = Stream(2L, 3L, 5L, 7L, ...)
 * Stream.iterate(2L, PrimeNumbers::nextPrimeFrom)
 *
 * // helpers
 *
 * static long nextPrimeFrom(long num) {
 *     return Stream.from(num + 1).find(PrimeNumbers::isPrime).get();
 * }
 *
 * static boolean isPrime(long num) {
 *     return !Stream.rangeClosed(2L, (long) Math.sqrt(num)).exists(d -> num % d == 0);
 * }
 * 
 * 
* * See Okasaki, Chris: Purely Functional Data Structures (p. 34 ff.). Cambridge, 2003. * * @param component type of this Stream * @author Daniel Dietrich, Jörgen Andersson, Ruslan Sennov * @since 1.1.0 */ public interface Stream extends Kind1, T>, LinearSeq { long serialVersionUID = 1L; /** * Returns a {@link java.util.stream.Collector} which may be used in conjunction with * {@link java.util.stream.Stream#collect(java.util.stream.Collector)} to obtain a {@link javaslang.collection.Stream}. * * @param Component type of the Stream. * @return A javaslang.collection.Stream Collector. */ static Collector, Stream> collector() { final Supplier> supplier = ArrayList::new; final BiConsumer, T> accumulator = ArrayList::add; final BinaryOperator> combiner = (left, right) -> { left.addAll(right); return left; }; final Function, Stream> finisher = Stream::ofAll; return Collector.of(supplier, accumulator, combiner, finisher); } /** * Returns an infinitely long Stream of {@code int} values starting from {@code from}. *

* The {@code Stream} extends to {@code Integer.MIN_VALUE} when passing {@code Integer.MAX_VALUE}. * * @param value a start int value * @return a new Stream of int values starting from {@code from} */ static Stream from(int value) { return Stream.ofAll(Iterator.from(value)); } /** * Returns an infinite long Stream of {@code int} values starting from {@code value} and spaced by {@code step}. *

* The {@code Stream} extends to {@code Integer.MIN_VALUE} when passing {@code Integer.MAX_VALUE}. * * @param value a start int value * @param step the step by which to advance on each next value * @return a new {@code Stream} of int values starting from {@code from} */ static Stream from(int value, int step) { return Stream.ofAll(Iterator.from(value, step)); } /** * Returns an infinitely long Stream of {@code long} values starting from {@code from}. *

* The {@code Stream} extends to {@code Integer.MIN_VALUE} when passing {@code Long.MAX_VALUE}. * * @param value a start long value * @return a new Stream of long values starting from {@code from} */ static Stream from(long value) { return Stream.ofAll(Iterator.from(value)); } /** * Returns an infinite long Stream of {@code long} values starting from {@code value} and spaced by {@code step}. *

* The {@code Stream} extends to {@code Long.MIN_VALUE} when passing {@code Long.MAX_VALUE}. * * @param value a start long value * @param step the step by which to advance on each next value * @return a new {@code Stream} of long values starting from {@code from} */ static Stream from(long value, long step) { return Stream.ofAll(Iterator.from(value, step)); } /** * Generates an (theoretically) infinitely long Stream using a value Supplier. * * @param supplier A Supplier of Stream values * @param value type * @return A new Stream */ static Stream continually(Supplier supplier) { Objects.requireNonNull(supplier, "supplier is null"); return Stream.ofAll(Iterator.continually(supplier)); } /** * Generates a (theoretically) infinitely long Stream using a function to calculate the next value * based on the previous. * * @param seed The first value in the Stream * @param f A function to calculate the next value based on the previous * @param value type * @return A new Stream */ static Stream iterate(T seed, Function f) { Objects.requireNonNull(f, "f is null"); return Stream.ofAll(Iterator.iterate(seed, f)); } /** * Constructs a Stream of a head element and a tail supplier. * * @param head The head element of the Stream * @param tailSupplier A supplier of the tail values. To end the stream, return {@link Stream#empty}. * @param value type * @return A new Stream */ @SuppressWarnings("unchecked") static Stream cons(T head, Supplier> tailSupplier) { Objects.requireNonNull(tailSupplier, "tailSupplier is null"); return new ConsImpl<>(head, (Supplier>) tailSupplier); } /** * Returns the single instance of Nil. Convenience method for {@code Nil.instance()}. *

* Note: this method intentionally returns type {@code Stream} and not {@code Nil}. This comes handy when folding. * If you explicitly need type {@code Nil} use {@linkplain Empty#instance()}. * * @param Component type of Nil, determined by type inference in the particular context. * @return The empty list. */ static Stream empty() { return Empty.instance(); } /** * Narrows a widened {@code Stream} to {@code Stream} * by performing a type safe-cast. This is eligible because immutable/read-only * collections are covariant. * * @param stream A {@code Stream}. * @param Component type of the {@code Stream}. * @return the given {@code stream} instance as narrowed type {@code Stream}. */ @SuppressWarnings("unchecked") static Stream narrow(Stream stream) { return (Stream) stream; } /** * Returns a singleton {@code Stream}, i.e. a {@code Stream} of one element. * * @param element An element. * @param The component type * @return A new Stream instance containing the given element */ static Stream of(T element) { return cons(element, Empty::instance); } /** * Creates a Stream of the given elements. * *

  Stream.of(1, 2, 3, 4)
     * = Nil.instance().prepend(4).prepend(3).prepend(2).prepend(1)
     * = new Cons(1, new Cons(2, new Cons(3, new Cons(4, Nil.instance()))))
* * @param Component type of the Stream. * @param elements Zero or more elements. * @return A list containing the given elements in the same order. */ @SafeVarargs static Stream of(T... elements) { Objects.requireNonNull(elements, "elements is null"); return Stream.ofAll(new Iterator() { int i = 0; @Override public boolean hasNext() { return i < elements.length; } @Override public T next() { return elements[i++]; } }); } /** * Returns a Stream containing {@code n} values of a given Function {@code f} * over a range of integer values from 0 to {@code n - 1}. * * @param Component type of the Stream * @param n The number of elements in the Stream * @param f The Function computing element values * @return A Stream consisting of elements {@code f(0),f(1), ..., f(n - 1)} * @throws NullPointerException if {@code f} is null */ static Stream tabulate(int n, Function f) { Objects.requireNonNull(f, "f is null"); return Stream.ofAll(Collections.tabulate(n, f)); } /** * Returns a Stream containing {@code n} values supplied by a given Supplier {@code s}. * * @param Component type of the Stream * @param n The number of elements in the Stream * @param s The Supplier computing element values * @return A Stream of size {@code n}, where each element contains the result supplied by {@code s}. * @throws NullPointerException if {@code s} is null */ static Stream fill(int n, Supplier s) { Objects.requireNonNull(s, "s is null"); return Stream.ofAll(Collections.fill(n, s)); } /** * Creates a Stream of the given elements. * * @param Component type of the Stream. * @param elements An Iterable of elements. * @return A list containing the given elements in the same order. */ @SuppressWarnings("unchecked") static Stream ofAll(Iterable elements) { Objects.requireNonNull(elements, "elements is null"); if (elements instanceof Stream) { return (Stream) elements; } else { return StreamFactory.create(elements.iterator()); } } /** * Creates a Stream based on the elements of a boolean array. * * @param array a boolean array * @return A new Stream of Boolean values */ static Stream ofAll(boolean[] array) { Objects.requireNonNull(array, "array is null"); return Stream.ofAll(Iterator.ofAll(array)); } /** * Creates a Stream based on the elements of a byte array. * * @param array a byte array * @return A new Stream of Byte values */ static Stream ofAll(byte[] array) { Objects.requireNonNull(array, "array is null"); return Stream.ofAll(Iterator.ofAll(array)); } /** * Creates a Stream based on the elements of a char array. * * @param array a char array * @return A new Stream of Character values */ static Stream ofAll(char[] array) { Objects.requireNonNull(array, "array is null"); return Stream.ofAll(Iterator.ofAll(array)); } /** * Creates a Stream based on the elements of a double array. * * @param array a double array * @return A new Stream of Double values */ static Stream ofAll(double[] array) { Objects.requireNonNull(array, "array is null"); return Stream.ofAll(Iterator.ofAll(array)); } /** * Creates a Stream based on the elements of a float array. * * @param array a float array * @return A new Stream of Float values */ static Stream ofAll(float[] array) { Objects.requireNonNull(array, "array is null"); return Stream.ofAll(Iterator.ofAll(array)); } /** * Creates a Stream based on the elements of an int array. * * @param array an int array * @return A new Stream of Integer values */ static Stream ofAll(int[] array) { Objects.requireNonNull(array, "array is null"); return Stream.ofAll(Iterator.ofAll(array)); } /** * Creates a Stream based on the elements of a long array. * * @param array a long array * @return A new Stream of Long values */ static Stream ofAll(long[] array) { Objects.requireNonNull(array, "array is null"); return Stream.ofAll(Iterator.ofAll(array)); } /** * Creates a Stream based on the elements of a short array. * * @param array a short array * @return A new Stream of Short values */ static Stream ofAll(short[] array) { Objects.requireNonNull(array, "array is null"); return Stream.ofAll(Iterator.ofAll(array)); } static Stream range(char from, char toExclusive) { return Stream.ofAll(Iterator.range(from, toExclusive)); } static Stream rangeBy(char from, char toExclusive, int step) { return Stream.ofAll(Iterator.rangeBy(from, toExclusive, step)); } static Stream rangeBy(double from, double toExclusive, double step) { return Stream.ofAll(Iterator.rangeBy(from, toExclusive, step)); } /** * Creates a Stream of int numbers starting from {@code from}, extending to {@code toExclusive - 1}. *

* Examples: *

     * 
     * Stream.range(0, 0)  // = Stream()
     * Stream.range(2, 0)  // = Stream()
     * Stream.range(-2, 2) // = Stream(-2, -1, 0, 1)
     * 
     * 
* * @param from the first number * @param toExclusive the last number + 1 * @return a range of int values as specified or {@code Nil} if {@code from >= toExclusive} */ static Stream range(int from, int toExclusive) { return Stream.ofAll(Iterator.range(from, toExclusive)); } /** * Creates a Stream of int numbers starting from {@code from}, extending to {@code toExclusive - 1}, * with {@code step}. *

* Examples: *

     * 
     * Stream.rangeBy(1, 3, 1)  // = Stream(1, 2)
     * Stream.rangeBy(1, 4, 2)  // = Stream(1, 3)
     * Stream.rangeBy(4, 1, -2) // = Stream(4, 2)
     * Stream.rangeBy(4, 1, 2)  // = Stream()
     * 
     * 
* * @param from the first number * @param toExclusive the last number + 1 * @param step the step * @return a range of long values as specified or {@code Nil} if
* {@code from >= toInclusive} and {@code step > 0} or
* {@code from <= toInclusive} and {@code step < 0} * @throws IllegalArgumentException if {@code step} is zero */ static Stream rangeBy(int from, int toExclusive, int step) { return Stream.ofAll(Iterator.rangeBy(from, toExclusive, step)); } /** * Creates a Stream of long numbers starting from {@code from}, extending to {@code toExclusive - 1}. *

* Examples: *

     * 
     * Stream.range(0L, 0L)  // = Stream()
     * Stream.range(2L, 0L)  // = Stream()
     * Stream.range(-2L, 2L) // = Stream(-2L, -1L, 0L, 1L)
     * 
     * 
* * @param from the first number * @param toExclusive the last number + 1 * @return a range of long values as specified or {@code Nil} if {@code from >= toExclusive} */ static Stream range(long from, long toExclusive) { return Stream.ofAll(Iterator.range(from, toExclusive)); } /** * Creates a Stream of long numbers starting from {@code from}, extending to {@code toExclusive - 1}, * with {@code step}. *

* Examples: *

     * 
     * Stream.rangeBy(1L, 3L, 1L)  // = Stream(1L, 2L)
     * Stream.rangeBy(1L, 4L, 2L)  // = Stream(1L, 3L)
     * Stream.rangeBy(4L, 1L, -2L) // = Stream(4L, 2L)
     * Stream.rangeBy(4L, 1L, 2L)  // = Stream()
     * 
     * 
* * @param from the first number * @param toExclusive the last number + 1 * @param step the step * @return a range of long values as specified or {@code Nil} if
* {@code from >= toInclusive} and {@code step > 0} or
* {@code from <= toInclusive} and {@code step < 0} * @throws IllegalArgumentException if {@code step} is zero */ static Stream rangeBy(long from, long toExclusive, long step) { return Stream.ofAll(Iterator.rangeBy(from, toExclusive, step)); } static Stream rangeClosed(char from, char toInclusive) { return Stream.ofAll(Iterator.rangeClosed(from, toInclusive)); } static Stream rangeClosedBy(char from, char toInclusive, int step) { return Stream.ofAll(Iterator.rangeClosedBy(from, toInclusive, step)); } static Stream rangeClosedBy(double from, double toInclusive, double step) { return Stream.ofAll(Iterator.rangeClosedBy(from, toInclusive, step)); } /** * Creates a Stream of int numbers starting from {@code from}, extending to {@code toInclusive}. *

* Examples: *

     * 
     * Stream.rangeClosed(0, 0)  // = Stream(0)
     * Stream.rangeClosed(2, 0)  // = Stream()
     * Stream.rangeClosed(-2, 2) // = Stream(-2, -1, 0, 1, 2)
     * 
     * 
* * @param from the first number * @param toInclusive the last number * @return a range of int values as specified or {@code Nil} if {@code from > toInclusive} */ static Stream rangeClosed(int from, int toInclusive) { return Stream.ofAll(Iterator.rangeClosed(from, toInclusive)); } /** * Creates a Stream of int numbers starting from {@code from}, extending to {@code toInclusive}, * with {@code step}. *

* Examples: *

     * 
     * Stream.rangeClosedBy(1, 3, 1)  // = Stream(1, 2, 3)
     * Stream.rangeClosedBy(1, 4, 2)  // = Stream(1, 3)
     * Stream.rangeClosedBy(4, 1, -2) // = Stream(4, 2)
     * Stream.rangeClosedBy(4, 1, 2)  // = Stream()
     * 
     * 
* * @param from the first number * @param toInclusive the last number * @param step the step * @return a range of int values as specified or {@code Nil} if
* {@code from > toInclusive} and {@code step > 0} or
* {@code from < toInclusive} and {@code step < 0} * @throws IllegalArgumentException if {@code step} is zero */ static Stream rangeClosedBy(int from, int toInclusive, int step) { return Stream.ofAll(Iterator.rangeClosedBy(from, toInclusive, step)); } /** * Creates a Stream of long numbers starting from {@code from}, extending to {@code toInclusive}. *

* Examples: *

     * 
     * Stream.rangeClosed(0L, 0L)  // = Stream(0L)
     * Stream.rangeClosed(2L, 0L)  // = Stream()
     * Stream.rangeClosed(-2L, 2L) // = Stream(-2L, -1L, 0L, 1L, 2L)
     * 
     * 
* * @param from the first number * @param toInclusive the last number * @return a range of long values as specified or {@code Nil} if {@code from > toInclusive} */ static Stream rangeClosed(long from, long toInclusive) { return Stream.ofAll(Iterator.rangeClosed(from, toInclusive)); } /** * Creates a Stream of long numbers starting from {@code from}, extending to {@code toInclusive}, * with {@code step}. *

* Examples: *

     * 
     * Stream.rangeClosedBy(1L, 3L, 1L)  // = Stream(1L, 2L, 3L)
     * Stream.rangeClosedBy(1L, 4L, 2L)  // = Stream(1L, 3L)
     * Stream.rangeClosedBy(4L, 1L, -2L) // = Stream(4L, 2L)
     * Stream.rangeClosedBy(4L, 1L, 2L)  // = Stream()
     * 
     * 
* * @param from the first number * @param toInclusive the last number * @param step the step * @return a range of int values as specified or {@code Nil} if
* {@code from > toInclusive} and {@code step > 0} or
* {@code from < toInclusive} and {@code step < 0} * @throws IllegalArgumentException if {@code step} is zero */ static Stream rangeClosedBy(long from, long toInclusive, long step) { return Stream.ofAll(Iterator.rangeClosedBy(from, toInclusive, step)); } /** * Repeats an element infinitely often. * * @param t An element * @param Element type * @return A new Stream containing infinite {@code t}'s. */ static Stream continually(T t) { return Stream.ofAll(Iterator.continually(t)); } @Override default Stream append(T element) { return isEmpty() ? Stream.of(element) : new AppendElements<>(head(), Queue.of(element), this::tail); } @Override default Stream appendAll(Iterable elements) { Objects.requireNonNull(elements, "elements is null"); return Stream.ofAll(isEmpty() ? elements : Iterator.concat(this, elements)); } /** * Appends itself to the end of stream with {@code mapper} function. *

* Example: *

* Well known Scala code for Fibonacci infinite sequence *

     * 
     * val fibs:Stream[Int] = 0 #:: 1 #:: (fibs zip fibs.tail).map{ t => t._1 + t._2 }
     * 
     * 
* can be transformed to *
     * 
     * Stream.of(0, 1).appendSelf(self -> self.zip(self.tail()).map(t -> t._1 + t._2));
     * 
     * 
* * @param mapper an mapper * @return a new Stream */ default Stream appendSelf(Function, ? extends Stream> mapper) { Objects.requireNonNull(mapper, "mapper is null"); return isEmpty() ? this : new AppendSelf<>((Cons) this, mapper).stream(); } @Override default Stream> combinations() { return Stream.rangeClosed(0, length()).map(this::combinations).flatMap(Function.identity()); } @Override default Stream> combinations(int k) { return Combinations.apply(this, Math.max(k, 0)); } @Override default Iterator> crossProduct(int power) { return Collections.crossProduct(Stream.empty(), this, power); } /** * Repeat the elements of this Stream infinitely. *

* Example: *

     * 
     * // = 1, 2, 3, 1, 2, 3, 1, 2, 3, ...
     * Stream.of(1, 2, 3).cycle();
     * 
     * 
* * @return A new Stream containing this elements cycled. */ default Stream cycle() { return isEmpty() ? this : appendSelf(Function.identity()); } /** * Repeat the elements of this Stream {@code count} times. *

* Example: *

     * 
     * // = empty
     * Stream.of(1, 2, 3).cycle(0);
     *
     * // = 1, 2, 3
     * Stream.of(1, 2, 3).cycle(1);
     *
     * // = 1, 2, 3, 1, 2, 3, 1, 2, 3
     * Stream.of(1, 2, 3).cycle(3);
     * 
     * 
* * @param count the number of cycles to be performed * @return A new Stream containing this elements cycled {@code count} times. */ default Stream cycle(int count) { if (count <= 0 || isEmpty()) { return empty(); } else { final Stream self = this; return Stream.ofAll(new Iterator() { Stream stream = self; int i = count - 1; @Override public boolean hasNext() { return !stream.isEmpty() || i > 0; } @Override public T next() { if (stream.isEmpty()) { i--; stream = self; } final T result = stream.head(); stream = stream.tail(); return result; } }); } } @Override default Stream distinct() { return distinctBy(Function.identity()); } @Override default Stream distinctBy(Comparator comparator) { Objects.requireNonNull(comparator, "comparator is null"); final java.util.Set seen = new java.util.TreeSet<>(comparator); return filter(seen::add); } @Override default Stream distinctBy(Function keyExtractor) { final java.util.Set seen = new java.util.HashSet<>(); return filter(t -> seen.add(keyExtractor.apply(t))); } @Override default Stream drop(long n) { Stream stream = this; while (n-- > 0 && !stream.isEmpty()) { stream = stream.tail(); } return stream; } @Override default Stream dropRight(long n) { if (n <= 0) { return this; } else { return DropRight.apply(take(n).toList(), List.empty(), drop(n)); } } @Override default Stream dropUntil(Predicate predicate) { Objects.requireNonNull(predicate, "predicate is null"); return dropWhile(predicate.negate()); } @Override default Stream dropWhile(Predicate predicate) { Objects.requireNonNull(predicate, "predicate is null"); Stream stream = this; while (!stream.isEmpty() && predicate.test(stream.head())) { stream = stream.tail(); } return stream; } @Override default Stream filter(Predicate predicate) { Objects.requireNonNull(predicate, "predicate is null"); Stream stream = this; while (!stream.isEmpty() && !predicate.test(stream.head())) { stream = stream.tail(); } final Stream finalStream = stream; return stream.isEmpty() ? stream : cons(stream.head(), () -> finalStream.tail().filter(predicate)); } @Override default Stream flatMap(Function> mapper) { Objects.requireNonNull(mapper, "mapper is null"); return isEmpty() ? Empty.instance() : Stream.ofAll(new Iterator() { final Iterator inputs = Stream.this.iterator(); java.util.Iterator current = java.util.Collections.emptyIterator(); @Override public boolean hasNext() { boolean currentHasNext; while (!(currentHasNext = current.hasNext()) && inputs.hasNext()) { current = mapper.apply(inputs.next()).iterator(); } return currentHasNext; } @Override public U next() { return current.next(); } }); } @Override default T get(int index) { if (isEmpty()) { throw new IndexOutOfBoundsException("get(" + index + ") on Nil"); } if (index < 0) { throw new IndexOutOfBoundsException("get(" + index + ")"); } Stream stream = this; for (int i = index - 1; i >= 0; i--) { stream = stream.tail(); if (stream.isEmpty()) { throw new IndexOutOfBoundsException(String.format("get(%s) on Stream of size %s", index, index - i)); } } return stream.head(); } @Override default Map> groupBy(Function classifier) { Objects.requireNonNull(classifier, "classifier is null"); return iterator().groupBy(classifier).map((c, it) -> Tuple.of(c, Stream.ofAll(it))); } @Override default Iterator> grouped(long size) { return sliding(size, size); } @Override default boolean hasDefiniteSize() { return false; } @Override default Option headOption() { return isEmpty() ? Option.none() : Option.some(head()); } @Override default int indexOf(T element, int from) { int index = 0; for (Stream stream = this; !stream.isEmpty(); stream = stream.tail(), index++) { if (index >= from && Objects.equals(stream.head(), element)) { return index; } } return -1; } @Override default Stream init() { if (isEmpty()) { throw new UnsupportedOperationException("init of empty stream"); } else { final Stream tail = tail(); if (tail.isEmpty()) { return Empty.instance(); } else { return cons(head(), tail::init); } } } @Override default Option> initOption() { return isEmpty() ? Option.none() : Option.some(init()); } @Override default Stream insert(int index, T element) { if (index < 0) { throw new IndexOutOfBoundsException("insert(" + index + ", e)"); } else if (index == 0) { return cons(element, () -> this); } else if (isEmpty()) { throw new IndexOutOfBoundsException("insert(" + index + ", e) on Nil"); } else { return cons(head(), () -> tail().insert(index - 1, element)); } } @SuppressWarnings("unchecked") @Override default Stream insertAll(int index, Iterable elements) { Objects.requireNonNull(elements, "elements is null"); if (index < 0) { throw new IndexOutOfBoundsException("insertAll(" + index + ", elements)"); } else if (index == 0) { return isEmpty() ? Stream.ofAll(elements) : Stream.ofAll((Iterable) elements).appendAll(this); } else if (isEmpty()) { throw new IndexOutOfBoundsException("insertAll(" + index + ", elements) on Nil"); } else { return cons(head(), () -> tail().insertAll(index - 1, elements)); } } @Override default Stream intersperse(T element) { if (isEmpty()) { return this; } else { return cons(head(), () -> { final Stream tail = tail(); return tail.isEmpty() ? tail : cons(element, () -> tail.intersperse(element)); }); } } @Override default boolean isTraversableAgain() { return true; } @Override default int lastIndexOf(T element, int end) { int result = -1, index = 0; for (Stream stream = this; index <= end && !stream.isEmpty(); stream = stream.tail(), index++) { if (Objects.equals(stream.head(), element)) { result = index; } } return result; } @Override default int length() { return foldLeft(0, (n, ignored) -> n + 1); } @Override default Stream map(Function mapper) { Objects.requireNonNull(mapper, "mapper is null"); if (isEmpty()) { return Empty.instance(); } else { return cons(mapper.apply(head()), () -> tail().map(mapper)); } } @Override default Stream padTo(int length, T element) { if (length <= 0) { return this; } else if (isEmpty()) { return Stream.ofAll(Iterator.continually(element).take(length)); } else { return cons(head(), () -> tail().padTo(length - 1, element)); } } @Override default Stream patch(int from, Iterable that, int replaced) { from = from < 0 ? 0 : from; replaced = replaced < 0 ? 0 : replaced; Stream result = take(from).appendAll(that); from += replaced; result = result.appendAll(drop(from)); return result; } @Override default Tuple2, Stream> partition(Predicate predicate) { Objects.requireNonNull(predicate, "predicate is null"); return Tuple.of(filter(predicate), filter(predicate.negate())); } @Override default Stream peek(Consumer action) { Objects.requireNonNull(action, "action is null"); if (isEmpty()) { return this; } else { final T head = head(); action.accept(head); return cons(head, () -> tail().peek(action)); } } @Override default Stream> permutations() { if (isEmpty()) { return Empty.instance(); } else { final Stream tail = tail(); if (tail.isEmpty()) { return Stream.of(this); } else { final Stream> zero = Empty.instance(); return distinct().foldLeft(zero, (xs, x) -> { final Function, Stream> prepend = l -> l.prepend(x); return xs.appendAll(remove(x).permutations().map(prepend)); }); } } } @Override default Stream prepend(T element) { return cons(element, () -> this); } @SuppressWarnings("unchecked") @Override default Stream prependAll(Iterable elements) { Objects.requireNonNull(elements, "elements is null"); return Stream.ofAll((Iterable) elements).appendAll(this); } @Override default Stream remove(T element) { if (isEmpty()) { return this; } else { final T head = head(); return Objects.equals(head, element) ? tail() : cons(head, () -> tail().remove(element)); } } @Override default Stream removeFirst(Predicate predicate) { Objects.requireNonNull(predicate, "predicate is null"); if (isEmpty()) { return this; } else { final T head = head(); return predicate.test(head) ? tail() : cons(head, () -> tail().removeFirst(predicate)); } } @Override default Stream removeLast(Predicate predicate) { return isEmpty() ? this : reverse().removeFirst(predicate).reverse(); } @Override default Stream removeAt(int index) { if (index < 0) { throw new IndexOutOfBoundsException("removeAt(" + index + ")"); } else if (index == 0) { return tail(); } else if (isEmpty()) { throw new IndexOutOfBoundsException("removeAt() on Nil"); } else { return cons(head(), () -> tail().removeAt(index - 1)); } } @Override default Stream removeAll(T removed) { return filter(e -> !Objects.equals(e, removed)); } @SuppressWarnings("unchecked") @Override default Stream removeAll(Iterable elements) { Objects.requireNonNull(elements, "elements is null"); final Stream distinct = Stream.ofAll((Iterable) elements).distinct(); return filter(e -> !distinct.contains(e)); } @Override default Stream replace(T currentElement, T newElement) { if (isEmpty()) { return this; } else { final T head = head(); if (Objects.equals(head, currentElement)) { return cons(newElement, this::tail); } else { return cons(head, () -> tail().replace(currentElement, newElement)); } } } @Override default Stream replaceAll(T currentElement, T newElement) { if (isEmpty()) { return this; } else { final T head = head(); final T newHead = Objects.equals(head, currentElement) ? newElement : head; return cons(newHead, () -> tail().replaceAll(currentElement, newElement)); } } @SuppressWarnings("unchecked") @Override default Stream retainAll(Iterable elements) { Objects.requireNonNull(elements, "elements is null"); if (isEmpty()) { return this; } else { final Stream retained = Stream.ofAll((Iterable) elements).distinct(); return filter(retained::contains); } } @Override default Stream reverse() { return isEmpty() ? this : foldLeft(Stream.empty(), Stream::prepend); } @Override default Stream scan(T zero, BiFunction operation) { return scanLeft(zero, operation); } @Override default Stream scanLeft(U zero, BiFunction operation) { Objects.requireNonNull(operation, "operation is null"); // lazily streams the elements of an iterator return Stream.ofAll(iterator().scanLeft(zero, operation)); } // not lazy! @Override default Stream scanRight(U zero, BiFunction operation) { Objects.requireNonNull(operation, "operation is null"); return Collections.scanRight(this, zero, operation, Stream.empty(), Stream::prepend, Function.identity()); } @Override default Stream slice(long beginIndex, long endIndex) { if (beginIndex >= endIndex || isEmpty()) { return empty(); } else { final long lowerBound = Math.max(beginIndex, 0); if (lowerBound == 0) { return cons(head(), () -> tail().slice(0, endIndex - 1)); } else { return tail().slice(lowerBound - 1, endIndex - 1); } } } @Override default Iterator> sliding(long size) { return sliding(size, 1); } @Override default Iterator> sliding(long size, long step) { return iterator().sliding(size, step).map(Stream::ofAll); } @Override default Stream sorted() { return isEmpty() ? this : toJavaStream().sorted().collect(Stream.collector()); } @Override default Stream sorted(Comparator comparator) { Objects.requireNonNull(comparator, "comparator is null"); return isEmpty() ? this : toJavaStream().sorted(comparator).collect(Stream.collector()); } @Override default > Stream sortBy(Function mapper) { return sortBy(U::compareTo, mapper); } @Override default Stream sortBy(Comparator comparator, Function mapper) { final Function domain = Function1.of(mapper::apply).memoized(); return toJavaStream() .sorted((e1, e2) -> comparator.compare(domain.apply(e1), domain.apply(e2))) .collect(collector()); } @Override default Tuple2, Stream> span(Predicate predicate) { Objects.requireNonNull(predicate, "predicate is null"); return Tuple.of(takeWhile(predicate), dropWhile(predicate)); } @Override default Tuple2, Stream> splitAt(long n) { return Tuple.of(take(n), drop(n)); } @Override default Tuple2, Stream> splitAt(Predicate predicate) { Objects.requireNonNull(predicate, "predicate is null"); return Tuple.of(takeWhile(predicate.negate()), dropWhile(predicate.negate())); } @Override default Tuple2, Stream> splitAtInclusive(Predicate predicate) { final Tuple2, Stream> split = splitAt(predicate); if (split._2.isEmpty()) { return split; } else { return Tuple.of(split._1.append(split._2.head()), split._2.tail()); } } @Override default Spliterator spliterator() { // the focus of the Stream API is on random-access collections of *known size* return Spliterators.spliterator(iterator(), length(), Spliterator.ORDERED | Spliterator.IMMUTABLE); } @Override default String stringPrefix() { return "Stream"; } @Override default Stream subSequence(int beginIndex) { if (beginIndex < 0) { throw new IndexOutOfBoundsException("subSequence(" + beginIndex + ")"); } Stream result = this; for (int i = 0; i < beginIndex; i++, result = result.tail()) { if (result.isEmpty()) { throw new IndexOutOfBoundsException( String.format("subSequence(%s) on Stream of size %s", beginIndex, i)); } } return result; } @Override default Stream subSequence(int beginIndex, int endIndex) { if (beginIndex < 0 || beginIndex > endIndex) { throw new IndexOutOfBoundsException(String.format("subSequence(%s, %s)", beginIndex, endIndex)); } if (beginIndex == endIndex) { return Empty.instance(); } else if (isEmpty()) { throw new IndexOutOfBoundsException("subSequence of Nil"); } else if (beginIndex == 0) { return cons(head(), () -> tail().subSequence(0, endIndex - 1)); } else { return tail().subSequence(beginIndex - 1, endIndex - 1); } } @Override Stream tail(); @Override default Option> tailOption() { return isEmpty() ? Option.none() : Option.some(tail()); } @Override default Stream take(long n) { if (n < 1 || isEmpty()) { return Empty.instance(); } else if (n == 1) { return cons(head(), Stream::empty); } else { return cons(head(), () -> tail().take(n - 1)); } } @Override default Stream takeRight(long n) { Stream right = this; Stream remaining = drop(n); while (!remaining.isEmpty()) { right = right.tail(); remaining = remaining.tail(); } return right; } @Override default Stream takeUntil(Predicate predicate) { Objects.requireNonNull(predicate, "predicate is null"); return takeWhile(predicate.negate()); } @Override default Stream takeWhile(Predicate predicate) { Objects.requireNonNull(predicate, "predicate is null"); if (isEmpty()) { return Empty.instance(); } else { final T head = head(); if (predicate.test(head)) { return cons(head, () -> tail().takeWhile(predicate)); } else { return Empty.instance(); } } } /** * Transforms this {@code Stream}. * * @param f A transformation * @param Type of transformation result * @return An instance of type {@code U} * @throws NullPointerException if {@code f} is null */ default U transform(Function, ? extends U> f) { Objects.requireNonNull(f, "f is null"); return f.apply(this); } @Override default Stream unit(Iterable iterable) { return Stream.ofAll(iterable); } @Override default Tuple2, Stream> unzip( Function> unzipper) { Objects.requireNonNull(unzipper, "unzipper is null"); final Stream> stream = map(unzipper); final Stream stream1 = stream.map(t -> t._1); final Stream stream2 = stream.map(t -> t._2); return Tuple.of(stream1, stream2); } @Override default Tuple3, Stream, Stream> unzip3( Function> unzipper) { Objects.requireNonNull(unzipper, "unzipper is null"); final Stream> stream = map(unzipper); final Stream stream1 = stream.map(t -> t._1); final Stream stream2 = stream.map(t -> t._2); final Stream stream3 = stream.map(t -> t._3); return Tuple.of(stream1, stream2, stream3); } @Override default Stream update(int index, T element) { if (isEmpty()) { throw new IndexOutOfBoundsException("update(" + index + ", e) on Nil"); } if (index < 0) { throw new IndexOutOfBoundsException("update(" + index + ", e)"); } Stream preceding = Empty.instance(); Stream tail = this; for (int i = index; i > 0; i--, tail = tail.tail()) { if (tail.isEmpty()) { throw new IndexOutOfBoundsException("update at " + index); } preceding = preceding.prepend(tail.head()); } if (tail.isEmpty()) { throw new IndexOutOfBoundsException("update at " + index); } // skip the current head element because it is replaced return preceding.reverse().appendAll(tail.tail().prepend(element)); } @Override default Stream> zip(Iterable iterable) { Objects.requireNonNull(iterable, "iterable is null"); return Stream.ofAll(iterator().zip(iterable)); } @Override default Stream> zipAll(Iterable iterable, T thisElem, U thatElem) { Objects.requireNonNull(iterable, "iterable is null"); return Stream.ofAll(iterator().zipAll(iterable, thisElem, thatElem)); } @Override default Stream> zipWithIndex() { return Stream.ofAll(iterator().zipWithIndex()); } /** * Extends (continues) this {@code Stream} with a constantly repeated value. * * @param next value with which the stream should be extended * @return new {@code Stream} composed from this stream extended with a Stream of provided value */ default Stream extend(T next) { return Stream.ofAll(this.appendAll(Stream.continually(next))); } /** * Extends (continues) this {@code Stream} with values provided by a {@code Supplier} * * @param nextSupplier a supplier which will provide values for extending a stream * @return new {@code Stream} composed from this stream extended with values provided by the supplier */ default Stream extend(Supplier nextSupplier) { Objects.requireNonNull(nextSupplier, "nextSupplier is null"); return Stream.ofAll(appendAll(Stream.continually(nextSupplier))); } /** * Extends (continues) this {@code Stream} with a Stream of values created by applying * consecutively provided {@code Function} to the last element of the original Stream. * * @param nextFunction a function which calculates the next value basing on the previous value * @return new {@code Stream} composed from this stream extended with values calculated by the provided function */ default Stream extend(Function nextFunction) { Objects.requireNonNull(nextFunction, "nextFunction is null"); if (isEmpty()) { return this; } else { final Stream that = this; return Stream.ofAll(new AbstractIterator() { Stream stream = that; T last = null; @Override protected T getNext() { if (stream.isEmpty()) { stream = Stream.iterate(nextFunction.apply(last), nextFunction); } last = stream.head(); stream = stream.tail(); return last; } @Override public boolean hasNext() { return true; } }); } } /** * The empty Stream. *

* This is a singleton, i.e. not Cloneable. * * @param Component type of the Stream. * @since 1.1.0 */ final class Empty implements Stream, Serializable { private static final long serialVersionUID = 1L; private static final Empty INSTANCE = new Empty<>(); // hidden private Empty() { } /** * Returns the singleton empty Stream instance. * * @param Component type of the Stream * @return The empty Stream */ @SuppressWarnings("unchecked") public static Empty instance() { return (Empty) INSTANCE; } @Override public T head() { throw new NoSuchElementException("head of empty stream"); } @Override public boolean isEmpty() { return true; } @Override public Iterator iterator() { return Iterator.empty(); } @Override public Stream tail() { throw new UnsupportedOperationException("tail of empty stream"); } @Override public boolean equals(Object o) { return o == this; } @Override public int hashCode() { return Traversable.hash(this); } @Override public String toString() { return stringPrefix() + "()"; } /** * Instance control for object serialization. * * @return The singleton instance of Nil. * @see java.io.Serializable */ private Object readResolve() { return INSTANCE; } } /** * Non-empty {@code Stream}, consisting of a {@code head}, and {@code tail}. * * @param Component type of the Stream. * @since 1.1.0 */ abstract class Cons implements Stream { private static final long serialVersionUID = 1L; final T head; final Lazy> tail; Cons(T head, Supplier> tail) { Objects.requireNonNull(tail, "tail is null"); this.head = head; this.tail = Lazy.of(tail); } @Override public T head() { return head; } @Override public boolean isEmpty() { return false; } @Override public Iterator iterator() { return new StreamIterator<>(this); } @Override public boolean equals(Object o) { if (o == this) { return true; } else if (o instanceof Stream) { Stream stream1 = this; Stream stream2 = (Stream) o; while (!stream1.isEmpty() && !stream2.isEmpty()) { final boolean isEqual = Objects.equals(stream1.head(), stream2.head()); if (!isEqual) { return false; } stream1 = stream1.tail(); stream2 = stream2.tail(); } return stream1.isEmpty() && stream2.isEmpty(); } else { return false; } } @Override public int hashCode() { return Traversable.hash(this); } @Override public String toString() { final StringBuilder builder = new StringBuilder(stringPrefix()).append("("); Stream stream = this; while (stream != null && !stream.isEmpty()) { final Cons cons = (Cons) stream; builder.append(cons.head); if (cons.tail.isEvaluated()) { stream = stream.tail(); if (!stream.isEmpty()) { builder.append(", "); } } else { builder.append(", ?"); stream = null; } } return builder.append(")").toString(); } } } interface StreamModule { final class ConsImpl extends Cons implements Serializable { private static final long serialVersionUID = 1L; ConsImpl(T head, Supplier> tail) { super(head, tail); } @Override public Stream tail() { return tail.get(); } private Object writeReplace() { return new SerializationProxy<>(this); } private void readObject(ObjectInputStream stream) throws InvalidObjectException { throw new InvalidObjectException("Proxy required"); } } final class AppendElements extends Cons implements Serializable { private static final long serialVersionUID = 1L; private final Queue queue; AppendElements(T head, Queue queue, Supplier> tail) { super(head, tail); this.queue = queue; } @Override public Stream append(T element) { return new AppendElements<>(head, queue.append(element), tail); } @Override public Stream appendAll(Iterable elements) { Objects.requireNonNull(queue, "elements is null"); return isEmpty() ? Stream.ofAll(queue) : new AppendElements<>(head, queue.appendAll(elements), tail); } @Override public Stream tail() { Stream t = tail.get(); if (t.isEmpty()) { return Stream.ofAll(queue); } else { if (t instanceof ConsImpl) { ConsImpl c = (ConsImpl) t; return new AppendElements<>(c.head(), queue, c.tail); } else { AppendElements a = (AppendElements) t; return new AppendElements<>(a.head(), a.queue.appendAll(queue), a.tail); } } } private Object writeReplace() { return new SerializationProxy<>(this); } private void readObject(ObjectInputStream stream) throws InvalidObjectException { throw new InvalidObjectException("Proxy required"); } } /** * A serialization proxy which, in this context, is used to deserialize immutable, linked Streams with final * instance fields. * * @param The component type of the underlying stream. */ // DEV NOTE: The serialization proxy pattern is not compatible with non-final, i.e. extendable, // classes. Also, it may not be compatible with circular object graphs. final class SerializationProxy implements Serializable { private static final long serialVersionUID = 1L; // the instance to be serialized/deserialized private transient Cons stream; /** * Constructor for the case of serialization. *

* The constructor of a SerializationProxy takes an argument that concisely represents the logical state of * an instance of the enclosing class. * * @param stream a Cons */ SerializationProxy(Cons stream) { this.stream = stream; } /** * Write an object to a serialization stream. * * @param s An object serialization stream. * @throws java.io.IOException If an error occurs writing to the stream. */ private void writeObject(ObjectOutputStream s) throws IOException { s.defaultWriteObject(); s.writeInt(stream.length()); for (Stream l = stream; !l.isEmpty(); l = l.tail()) { s.writeObject(l.head()); } } /** * Read an object from a deserialization stream. * * @param s An object deserialization stream. * @throws ClassNotFoundException If the object's class read from the stream cannot be found. * @throws InvalidObjectException If the stream contains no stream elements. * @throws IOException If an error occurs reading from the stream. */ private void readObject(ObjectInputStream s) throws ClassNotFoundException, IOException { s.defaultReadObject(); final int size = s.readInt(); if (size <= 0) { throw new InvalidObjectException("No elements"); } Stream temp = Empty.instance(); for (int i = 0; i < size; i++) { @SuppressWarnings("unchecked") final T element = (T) s.readObject(); temp = temp.append(element); } // DEV-NOTE: Cons is deserialized stream = (Cons) temp; } /** * {@code readResolve} method for the serialization proxy pattern. *

* Returns a logically equivalent instance of the enclosing class. The presence of this method causes the * serialization system to translate the serialization proxy back into an instance of the enclosing class * upon deserialization. * * @return A deserialized instance of the enclosing class. */ private Object readResolve() { return stream; } } final class AppendSelf { private final Cons self; AppendSelf(Cons self, Function, ? extends Stream> mapper) { this.self = appendAll(self, mapper); } private Cons appendAll(Cons stream, Function, ? extends Stream> mapper) { return (Cons) Stream.cons(stream.head(), () -> { final Stream tail = stream.tail(); return tail.isEmpty() ? mapper.apply(self) : appendAll((Cons) tail, mapper); }); } Cons stream() { return self; } } interface Combinations { static Stream> apply(Stream elements, int k) { if (k == 0) { return Stream.of(Stream.empty()); } else { return elements.zipWithIndex().flatMap( t -> apply(elements.drop(t._2 + 1), (k - 1)).map((Stream c) -> c.prepend(t._1)) ); } } } interface DropRight { // works with infinite streams by buffering elements static Stream apply(List front, List rear, Stream remaining) { if (remaining.isEmpty()) { return remaining; } else if (front.isEmpty()) { return apply(rear.reverse(), List.empty(), remaining); } else { return Stream.cons(front.head(), () -> apply(front.tail(), rear.prepend(remaining.head()), remaining.tail())); } } } interface StreamFactory { static Stream create(java.util.Iterator iterator) { return iterator.hasNext() ? Stream.cons(iterator.next(), () -> create(iterator)) : Empty.instance(); } } final class StreamIterator extends AbstractIterator { private Supplier> current; StreamIterator(Cons stream) { this.current = () -> stream; } @Override public boolean hasNext() { return !current.get().isEmpty(); } @Override public T getNext() { final Stream stream = current.get(); // DEV-NOTE: we make the stream even more lazy because the next head must not be evaluated on hasNext() current = stream::tail; return stream.head(); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy