/* __ __ __ __ __ ___
* \ \ / / \ \ / / __/
* \ \/ / /\ \ \/ / /
* \____/__/ \__\____/__/.ɪᴏ
* ᶜᵒᵖʸʳᶦᵍʰᵗ ᵇʸ ᵛᵃᵛʳ ⁻ ˡᶦᶜᵉⁿˢᵉᵈ ᵘⁿᵈᵉʳ ᵗʰᵉ ᵃᵖᵃᶜʰᵉ ˡᶦᶜᵉⁿˢᵉ ᵛᵉʳˢᶦᵒⁿ ᵗʷᵒ ᵈᵒᵗ ᶻᵉʳᵒ
*/
package io.vavr.collection;
import io.vavr.*;
import io.vavr.control.Option;
import io.vavr.collection.List.Nil;
import io.vavr.collection.ListModule.Combinations;
import io.vavr.collection.ListModule.SplitAt;
import java.io.*;
import java.util.*;
import java.util.function.*;
import java.util.stream.Collector;
import static io.vavr.collection.JavaConverters.ChangePolicy.IMMUTABLE;
import static io.vavr.collection.JavaConverters.ChangePolicy.MUTABLE;
/**
* An immutable {@code List} is an eager sequence of elements. Its immutability makes it suitable for concurrent programming.
* <p>
* A {@code List} is composed of a {@code head} element and a {@code tail} {@code List}.
* <p>
* There are two implementations of the {@code List} interface:
*
* <ul>
* <li>{@link Nil}, which represents the empty {@code List}.</li>
* <li>{@link Cons}, which represents a {@code List} containing one or more elements.</li>
* </ul>
*
* A {@code List} is a {@code Stack} in the sense that it stores elements allowing a last-in-first-out (LIFO) retrieval.
* <p>
* Stack API:
*
* <ul>
* <li>{@link #peek()}</li>
* <li>{@link #peekOption()}</li>
* <li>{@link #pop()}</li>
* <li>{@link #popOption()}</li>
* <li>{@link #pop2()}</li>
* <li>{@link #pop2Option()}</li>
* <li>{@link #push(Object)}</li>
* <li>{@link #push(Object[])}</li>
* <li>{@link #pushAll(Iterable)}</li>
* </ul>
*
* Methods to obtain a {@code List}:
*
* <pre>
* <code>
* // factory methods
* List.empty() // = List.of() = Nil.instance()
* List.of(x) // = new Cons<>(x, Nil.instance())
* List.of(Object...) // e.g. List.of(1, 2, 3)
* List.ofAll(Iterable) // e.g. List.ofAll(Stream.of(1, 2, 3)) = 1, 2, 3
* List.ofAll(<primitive array>) // e.g. List.of(new int[] {1, 2, 3}) = 1, 2, 3
*
* // int sequences
* List.range(0, 3) // = 0, 1, 2
* List.rangeClosed(0, 3) // = 0, 1, 2, 3
* </code>
* </pre>
*
* Note: A {@code List} is primarily a {@code Seq} and extends {@code Stack} for technical reasons (so {@code Stack} does not need to wrap {@code List}).
* <p>
* If operating on a {@code List}, please prefer
*
* <ul>
* <li>{@link #prepend(Object)} over {@link #push(Object)}</li>
* <li>{@link #prependAll(Iterable)} over {@link #pushAll(Iterable)}</li>
* <li>{@link #tail()} over {@link #pop()}</li>
* <li>{@link #tailOption()} over {@link #popOption()}</li>
* </ul>
*
* Factory method applications:
*
* <pre>
* <code>
* List<Integer> s1 = List.of(1);
* List<Integer> s2 = List.of(1, 2, 3);
* // = List.of(new Integer[] {1, 2, 3});
*
* List<int[]> s3 = List.ofAll(1, 2, 3);
* List<List<Integer>> s4 = List.ofAll(List.of(1, 2, 3));
*
* List<Integer> s5 = List.ofAll(1, 2, 3);
* List<Integer> s6 = List.ofAll(List.of(1, 2, 3));
*
* // cuckoo's egg
* List<Integer[]> s7 = List.<Integer[]> of(new Integer[] {1, 2, 3});
* </code>
* </pre>
*
* Example: Converting a String to digits
*
* <pre>
* <code>
* // = List(1, 2, 3)
* List.of("123".toCharArray()).map(c -> Character.digit(c, 10))
* </code>
* </pre>
*
* See Okasaki, Chris: <em>Purely Functional Data Structures</em> (p. 7 ff.). Cambridge, 2003.
*
* @param <T> Component type of the List
* @author Daniel Dietrich
*/
public interface List<T> extends LinearSeq<T> {
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 List}.
*
* @param <T> Component type of the List.
* @return A io.vavr.collection.List Collector.
*/
static <T> Collector<T, ArrayList<T>, List<T>> collector() {
final Supplier<ArrayList<T>> supplier = ArrayList::new;
final BiConsumer<ArrayList<T>, T> accumulator = ArrayList::add;
final BinaryOperator<ArrayList<T>> combiner = (left, right) -> {
left.addAll(right);
return left;
};
final Function<ArrayList<T>, List<T>> finisher = List::ofAll;
return Collector.of(supplier, accumulator, combiner, finisher);
}
/**
* Returns the single instance of Nil. Convenience method for {@code Nil.instance()} .
* <p>
* Note: this method intentionally returns type {@code List} and not {@code Nil}. This comes handy when folding.
* If you explicitly need type {@code Nil} use {@linkplain Nil#instance()}.
*
* @param <T> Component type of Nil, determined by type inference in the particular context.
* @return The empty list.
*/
static <T> List<T> empty() {
return Nil.instance();
}
/**
* A {@code List} is computed synchronously.
*
* @return false
*/
@Override
default boolean isAsync() {
return false;
}
@Override
boolean isEmpty();
/**
* A {@code List} is computed eagerly.
*
* @return false
*/
@Override
default boolean isLazy() {
return false;
}
/**
* Narrows a widened {@code List<? extends T>} to {@code List<T>}
* by performing a type-safe cast. This is eligible because immutable/read-only
* collections are covariant.
*
* @param list A {@code List}.
* @param <T> Component type of the {@code List}.
* @return the given {@code list} instance as narrowed type {@code List<T>}.
*/
@SuppressWarnings("unchecked")
static <T> List<T> narrow(List<? extends T> list) {
return (List<T>) list;
}
/**
* Returns a singleton {@code List}, i.e. a {@code List} of one element.
*
* @param element An element.
* @param <T> The component type
* @return A new List instance containing the given element
*/
static <T> List<T> of(T element) {
return new Cons<>(element, Nil.instance());
}
/**
* Creates a List of the given elements.
* <pre>
* <code>
* List.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()))))
* </code>
* </pre>
*
* @param <T> Component type of the List.
* @param elements Zero or more elements.
* @return A list containing the given elements in the same order.
* @throws NullPointerException if {@code elements} is null
*/
@SafeVarargs
static <T> List<T> of(T... elements) {
Objects.requireNonNull(elements, "elements is null");
List<T> result = Nil.instance();
for (int i = elements.length - 1; i >= 0; i--) {
result = result.prepend(elements[i]);
}
return result;
}
/**
* Creates a List of the given elements.
* <p>
* The resulting list has the same iteration order as the given iterable of elements
* if the iteration order of the elements is stable.
*
* @param <T> Component type of the List.
* @param elements An Iterable of elements.
* @return A list containing the given elements in the same order.
* @throws NullPointerException if {@code elements} is null
*/
@SuppressWarnings("unchecked")
static <T> List<T> ofAll(Iterable<? extends T> elements) {
Objects.requireNonNull(elements, "elements is null");
if (elements instanceof List) {
return (List<T>) elements;
} else if (elements instanceof java.util.List) {
List<T> result = Nil.instance();
final java.util.List<T> list = (java.util.List<T>) elements;
final ListIterator<T> iterator = list.listIterator(list.size());
while (iterator.hasPrevious()) {
result = result.prepend(iterator.previous());
}
return result;
} else if (elements instanceof NavigableSet) {
List<T> result = Nil.instance();
final java.util.Iterator<T> iterator = ((NavigableSet<T>) elements).descendingIterator();
while (iterator.hasNext()) {
result = result.prepend(iterator.next());
}
return result;
} else {
List<T> result = Nil.instance();
for (T element : elements) {
result = result.prepend(element);
}
return result.reverse();
}
}
/**
* Creates a List that contains the elements of the given {@link java.util.stream.Stream}.
*
* @param javaStream A {@link java.util.stream.Stream}
* @param <T> Component type of the Stream.
* @return A List containing the given elements in the same order.
*/
static <T> List<T> ofAll(java.util.stream.Stream<? extends T> javaStream) {
Objects.requireNonNull(javaStream, "javaStream is null");
final java.util.Iterator<? extends T> iterator = javaStream.iterator();
List<T> list = List.empty();
while (iterator.hasNext()) {
list = list.prepend(iterator.next());
}
return list.reverse();
}
/**
* Creates a List from boolean values.
*
* @param elements boolean values
* @return A new List of Boolean values
* @throws NullPointerException if elements is null
*/
static List<Boolean> ofAll(boolean... elements) {
Objects.requireNonNull(elements, "elements is null");
return ofAll(Iterator.ofAll(elements));
}
/**
* Creates a List from byte values.
*
* @param elements byte values
* @return A new List of Byte values
* @throws NullPointerException if elements is null
*/
static List<Byte> ofAll(byte... elements) {
Objects.requireNonNull(elements, "elements is null");
return ofAll(Iterator.ofAll(elements));
}
/**
* Creates a List from char values.
*
* @param elements char values
* @return A new List of Character values
* @throws NullPointerException if elements is null
*/
static List<Character> ofAll(char... elements) {
Objects.requireNonNull(elements, "elements is null");
return ofAll(Iterator.ofAll(elements));
}
/**
* Creates a List from double values.
*
* @param elements double values
* @return A new List of Double values
* @throws NullPointerException if elements is null
*/
static List<Double> ofAll(double... elements) {
Objects.requireNonNull(elements, "elements is null");
return ofAll(Iterator.ofAll(elements));
}
/**
* Creates a List from float values.
*
* @param elements a float values
* @return A new List of Float values
* @throws NullPointerException if elements is null
*/
static List<Float> ofAll(float... elements) {
Objects.requireNonNull(elements, "elements is null");
return ofAll(Iterator.ofAll(elements));
}
/**
* Creates a List from int values.
*
* @param elements int values
* @return A new List of Integer values
* @throws NullPointerException if elements is null
*/
static List<Integer> ofAll(int... elements) {
Objects.requireNonNull(elements, "elements is null");
return ofAll(Iterator.ofAll(elements));
}
/**
* Creates a List from long values.
*
* @param elements long values
* @return A new List of Long values
* @throws NullPointerException if elements is null
*/
static List<Long> ofAll(long... elements) {
Objects.requireNonNull(elements, "elements is null");
return ofAll(Iterator.ofAll(elements));
}
/**
* Creates a List from short values.
*
* @param elements short values
* @return A new List of Short values
* @throws NullPointerException if elements is null
*/
static List<Short> ofAll(short... elements) {
Objects.requireNonNull(elements, "elements is null");
return ofAll(Iterator.ofAll(elements));
}
/**
* Returns a List containing {@code n} values of a given Function {@code f}
* over a range of integer values from 0 to {@code n - 1}.
*
* @param <T> Component type of the List
* @param n The number of elements in the List
* @param f The Function computing element values
* @return A List consisting of elements {@code f(0),f(1), ..., f(n - 1)}
* @throws NullPointerException if {@code f} is null
*/
static <T> List<T> tabulate(int n, Function<? super Integer, ? extends T> f) {
Objects.requireNonNull(f, "f is null");
return Collections.tabulate(n, f, empty(), List::of);
}
/**
* Returns a List containing {@code n} values supplied by a given Supplier {@code s}.
*
* @param <T> Component type of the List
* @param n The number of elements in the List
* @param s The Supplier computing element values
* @return A List of size {@code n}, where each element contains the result supplied by {@code s}.
* @throws NullPointerException if {@code s} is null
*/
static <T> List<T> fill(int n, Supplier<? extends T> s) {
Objects.requireNonNull(s, "s is null");
return Collections.fill(n, s, empty(), List::of);
}
static List<Character> range(char from, char toExclusive) {
return ofAll(Iterator.range(from, toExclusive));
}
static List<Character> rangeBy(char from, char toExclusive, int step) {
return ofAll(Iterator.rangeBy(from, toExclusive, step));
}
@GwtIncompatible
static List<Double> rangeBy(double from, double toExclusive, double step) {
return ofAll(Iterator.rangeBy(from, toExclusive, step));
}
/**
* Creates a List of int numbers starting from {@code from}, extending to {@code toExclusive - 1}.
* <p>
* Examples:
* <pre>
* <code>
* List.range(0, 0) // = List()
* List.range(2, 0) // = List()
* List.range(-2, 2) // = List(-2, -1, 0, 1)
* </code>
* </pre>
*
* @param from the first number
* @param toExclusive the last number + 1
* @return a range of int values as specified or the empty range if {@code from >= toExclusive}
*/
static List<Integer> range(int from, int toExclusive) {
return ofAll(Iterator.range(from, toExclusive));
}
/**
* Creates a List of int numbers starting from {@code from}, extending to {@code toExclusive - 1},
* with {@code step}.
* <p>
* Examples:
* <pre>
* <code>
* List.rangeBy(1, 3, 1) // = List(1, 2)
* List.rangeBy(1, 4, 2) // = List(1, 3)
* List.rangeBy(4, 1, -2) // = List(4, 2)
* List.rangeBy(4, 1, 2) // = List()
* </code>
* </pre>
*
* @param from the first number
* @param toExclusive the last number + 1
* @param step the step
* @return a range of long values as specified or the empty range if<br>
* {@code from >= toInclusive} and {@code step > 0} or<br>
* {@code from <= toInclusive} and {@code step < 0}
* @throws IllegalArgumentException if {@code step} is zero
*/
static List<Integer> rangeBy(int from, int toExclusive, int step) {
return ofAll(Iterator.rangeBy(from, toExclusive, step));
}
/**
* Creates a List of long numbers starting from {@code from}, extending to {@code toExclusive - 1}.
* <p>
* Examples:
* <pre>
* <code>
* List.range(0L, 0L) // = List()
* List.range(2L, 0L) // = List()
* List.range(-2L, 2L) // = List(-2L, -1L, 0L, 1L)
* </code>
* </pre>
*
* @param from the first number
* @param toExclusive the last number + 1
* @return a range of long values as specified or the empty range if {@code from >= toExclusive}
*/
static List<Long> range(long from, long toExclusive) {
return ofAll(Iterator.range(from, toExclusive));
}
/**
* Creates a List of long numbers starting from {@code from}, extending to {@code toExclusive - 1},
* with {@code step}.
* <p>
* Examples:
* <pre>
* <code>
* List.rangeBy(1L, 3L, 1L) // = List(1L, 2L)
* List.rangeBy(1L, 4L, 2L) // = List(1L, 3L)
* List.rangeBy(4L, 1L, -2L) // = List(4L, 2L)
* List.rangeBy(4L, 1L, 2L) // = List()
* </code>
* </pre>
*
* @param from the first number
* @param toExclusive the last number + 1
* @param step the step
* @return a range of long values as specified or the empty range if<br>
* {@code from >= toInclusive} and {@code step > 0} or<br>
* {@code from <= toInclusive} and {@code step < 0}
* @throws IllegalArgumentException if {@code step} is zero
*/
static List<Long> rangeBy(long from, long toExclusive, long step) {
return ofAll(Iterator.rangeBy(from, toExclusive, step));
}
static List<Character> rangeClosed(char from, char toInclusive) {
return ofAll(Iterator.rangeClosed(from, toInclusive));
}
static List<Character> rangeClosedBy(char from, char toInclusive, int step) {
return ofAll(Iterator.rangeClosedBy(from, toInclusive, step));
}
@GwtIncompatible
static List<Double> rangeClosedBy(double from, double toInclusive, double step) {
return ofAll(Iterator.rangeClosedBy(from, toInclusive, step));
}
/**
* Creates a List of int numbers starting from {@code from}, extending to {@code toInclusive}.
* <p>
* Examples:
* <pre>
* <code>
* List.rangeClosed(0, 0) // = List(0)
* List.rangeClosed(2, 0) // = List()
* List.rangeClosed(-2, 2) // = List(-2, -1, 0, 1, 2)
* </code>
* </pre>
*
* @param from the first number
* @param toInclusive the last number
* @return a range of int values as specified or the empty range if {@code from > toInclusive}
*/
static List<Integer> rangeClosed(int from, int toInclusive) {
return ofAll(Iterator.rangeClosed(from, toInclusive));
}
/**
* Creates a List of int numbers starting from {@code from}, extending to {@code toInclusive},
* with {@code step}.
* <p>
* Examples:
* <pre>
* <code>
* List.rangeClosedBy(1, 3, 1) // = List(1, 2, 3)
* List.rangeClosedBy(1, 4, 2) // = List(1, 3)
* List.rangeClosedBy(4, 1, -2) // = List(4, 2)
* List.rangeClosedBy(4, 1, 2) // = List()
* </code>
* </pre>
*
* @param from the first number
* @param toInclusive the last number
* @param step the step
* @return a range of int values as specified or the empty range if<br>
* {@code from > toInclusive} and {@code step > 0} or<br>
* {@code from < toInclusive} and {@code step < 0}
* @throws IllegalArgumentException if {@code step} is zero
*/
static List<Integer> rangeClosedBy(int from, int toInclusive, int step) {
return ofAll(Iterator.rangeClosedBy(from, toInclusive, step));
}
/**
* Creates a List of long numbers starting from {@code from}, extending to {@code toInclusive}.
* <p>
* Examples:
* <pre>
* <code>
* List.rangeClosed(0L, 0L) // = List(0L)
* List.rangeClosed(2L, 0L) // = List()
* List.rangeClosed(-2L, 2L) // = List(-2L, -1L, 0L, 1L, 2L)
* </code>
* </pre>
*
* @param from the first number
* @param toInclusive the last number
* @return a range of long values as specified or the empty range if {@code from > toInclusive}
*/
static List<Long> rangeClosed(long from, long toInclusive) {
return ofAll(Iterator.rangeClosed(from, toInclusive));
}
/**
* Creates a List of long numbers starting from {@code from}, extending to {@code toInclusive},
* with {@code step}.
* <p>
* Examples:
* <pre>
* <code>
* List.rangeClosedBy(1L, 3L, 1L) // = List(1L, 2L, 3L)
* List.rangeClosedBy(1L, 4L, 2L) // = List(1L, 3L)
* List.rangeClosedBy(4L, 1L, -2L) // = List(4L, 2L)
* List.rangeClosedBy(4L, 1L, 2L) // = List()
* </code>
* </pre>
*
* @param from the first number
* @param toInclusive the last number
* @param step the step
* @return a range of int values as specified or the empty range if<br>
* {@code from > toInclusive} and {@code step > 0} or<br>
* {@code from < toInclusive} and {@code step < 0}
* @throws IllegalArgumentException if {@code step} is zero
*/
static List<Long> rangeClosedBy(long from, long toInclusive, long step) {
return ofAll(Iterator.rangeClosedBy(from, toInclusive, step));
}
/**
* Transposes the rows and columns of a {@link List} matrix.
*
* @param <T> matrix element type
* @param matrix to be transposed.
* @return a transposed {@link List} matrix.
* @throws IllegalArgumentException if the row lengths of {@code matrix} differ.
* <p>
* ex: {@code
* List.transpose(List(List(1,2,3), List(4,5,6))) → List(List(1,4), List(2,5), List(3,6))
* }
*/
static <T> List<List<T>> transpose(List<List<T>> matrix) {
return Collections.transpose(matrix, List::ofAll, List::of);
}
/**
* Creates a list from a seed value and a function.
* The function takes the seed at first.
* The function should return {@code None} when it's
* done generating the list, otherwise {@code Some} {@code Tuple}
* of the element for the next call and the value to add to the
* resulting list.
* <p>
* Example:
* <pre>
* <code>
* List.unfoldRight(10, x -> x == 0
* ? Option.none()
* : Option.of(new Tuple2<>(x, x-1)));
* // List(10, 9, 8, 7, 6, 5, 4, 3, 2, 1))
* </code>
* </pre>
*
* @param <T> type of seeds
* @param <U> type of unfolded values
* @param seed the start value for the iteration
* @param f the function to get the next step of the iteration
* @return a list with the values built up by the iteration
* @throws NullPointerException if {@code f} is null
*/
static <T, U> List<U> unfoldRight(T seed, Function<? super T, Option<Tuple2<? extends U, ? extends T>>> f) {
return Iterator.unfoldRight(seed, f).toList();
}
/**
* Creates a list from a seed value and a function.
* The function takes the seed at first.
* The function should return {@code None} when it's
* done generating the list, otherwise {@code Some} {@code Tuple}
* of the value to add to the resulting list and
* the element for the next call.
* <p>
* Example:
* <pre>
* <code>
* List.unfoldLeft(10, x -> x == 0
* ? Option.none()
* : Option.of(new Tuple2<>(x-1, x)));
* // List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10))
* </code>
* </pre>
*
* @param <T> type of seeds
* @param <U> type of unfolded values
* @param seed the start value for the iteration
* @param f the function to get the next step of the iteration
* @return a list with the values built up by the iteration
* @throws NullPointerException if {@code f} is null
*/
static <T, U> List<U> unfoldLeft(T seed, Function<? super T, Option<Tuple2<? extends T, ? extends U>>> f) {
return Iterator.unfoldLeft(seed, f).toList();
}
/**
* Creates a list from a seed value and a function.
* The function takes the seed at first.
* The function should return {@code None} when it's
* done generating the list, otherwise {@code Some} {@code Tuple}
* of the value to add to the resulting list and
* the element for the next call.
* <p>
* Example:
* <pre>
* <code>
* List.unfold(10, x -> x == 0
* ? Option.none()
* : Option.of(new Tuple2<>(x-1, x)));
* // List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10))
* </code>
* </pre>
*
* @param <T> type of seeds and unfolded values
* @param seed the start value for the iteration
* @param f the function to get the next step of the iteration
* @return a list with the values built up by the iteration
* @throws NullPointerException if {@code f} is null
*/
static <T> List<T> unfold(T seed, Function<? super T, Option<Tuple2<? extends T, ? extends T>>> f) {
return Iterator.unfold(seed, f).toList();
}
@Override
default List<T> append(T element) {
return foldRight(of(element), (x, xs) -> xs.prepend(x));
}
@Override
default List<T> appendAll(Iterable<? extends T> elements) {
Objects.requireNonNull(elements, "elements is null");
return List.<T> ofAll(elements).prependAll(this);
}
@GwtIncompatible
@Override
default java.util.List<T> asJava() {
return JavaConverters.asJava(this, IMMUTABLE);
}
@GwtIncompatible
@Override
default List<T> asJava(Consumer<? super java.util.List<T>> action) {
return Collections.asJava(this, action, IMMUTABLE);
}
@GwtIncompatible
@Override
default java.util.List<T> asJavaMutable() {
return JavaConverters.asJava(this, MUTABLE);
}
@GwtIncompatible
@Override
default List<T> asJavaMutable(Consumer<? super java.util.List<T>> action) {
return Collections.asJava(this, action, MUTABLE);
}
@Override
default <R> List<R> collect(PartialFunction<? super T, ? extends R> partialFunction) {
return ofAll(iterator().<R> collect(partialFunction));
}
@Override
default List<List<T>> combinations() {
return rangeClosed(0, length()).map(this::combinations).flatMap(Function.identity());
}
@Override
default List<List<T>> combinations(int k) {
return Combinations.apply(this, Math.max(k, 0));
}
@Override
default Iterator<List<T>> crossProduct(int power) {
return Collections.crossProduct(empty(), this, power);
}
@Override
default List<T> distinct() {
return distinctBy(Function.identity());
}
@Override
default List<T> distinctBy(Comparator<? super T> comparator) {
Objects.requireNonNull(comparator, "comparator is null");
final java.util.Set<T> seen = new java.util.TreeSet<>(comparator);
return filter(seen::add);
}
@Override
default <U> List<T> distinctBy(Function<? super T, ? extends U> keyExtractor) {
Objects.requireNonNull(keyExtractor, "keyExtractor is null");
final java.util.Set<U> seen = new java.util.HashSet<>();
return filter(t -> seen.add(keyExtractor.apply(t)));
}
@Override
default List<T> drop(int n) {
if (n <= 0) {
return this;
}
if (n >= size()) {
return empty();
}
List<T> list = this;
for (long i = n; i > 0 && !list.isEmpty(); i--) {
list = list.tail();
}
return list;
}
@Override
default List<T> dropUntil(Predicate<? super T> predicate) {
return Collections.dropUntil(this, predicate);
}
@Override
default List<T> dropWhile(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate, "predicate is null");
return dropUntil(predicate.negate());
}
@Override
default List<T> dropRight(int n) {
if (n <= 0) {
return this;
}
if (n >= length()) {
return empty();
}
return ofAll(iterator().dropRight(n));
}
@Override
default List<T> dropRightUntil(Predicate<? super T> predicate) {
return Collections.dropUntil(reverse(), predicate).reverse();
}
@Override
default List<T> dropRightWhile(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate, "predicate is null");
return dropRightUntil(predicate.negate());
}
@Override
default List<T> filter(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate, "predicate is null");
if (isEmpty()) {
return this;
} else {
final List<T> filtered = foldLeft(empty(), (xs, x) -> predicate.test(x) ? xs.prepend(x) : xs);
if (filtered.isEmpty()) {
return empty();
} else if (filtered.length() == length()) {
return this;
} else {
return filtered.reverse();
}
}
}
@Override
default <U> List<U> flatMap(Function<? super T, ? extends Iterable<? extends U>> mapper) {
Objects.requireNonNull(mapper, "mapper is null");
List<U> list = empty();
for (T t : this) {
for (U u : mapper.apply(t)) {
list = list.prepend(u);
}
}
return list.reverse();
}
@Override
default T get(int index) {
if (isEmpty()) {
throw new IndexOutOfBoundsException("get(" + index + ") on Nil");
}
if (index < 0) {
throw new IndexOutOfBoundsException("get(" + index + ")");
}
List<T> list = this;
for (int i = index - 1; i >= 0; i--) {
list = list.tail();
if (list.isEmpty()) {
throw new IndexOutOfBoundsException("get(" + index + ") on List of length " + (index - i));
}
}
return list.head();
}
@Override
default <C> Map<C, List<T>> groupBy(Function<? super T, ? extends C> classifier) {
return Collections.groupBy(this, classifier, List::ofAll);
}
@Override
default Iterator<List<T>> grouped(int size) {
return sliding(size, size);
}
@Override
default boolean hasDefiniteSize() {
return true;
}
@Override
default int indexOf(T element, int from) {
int index = 0;
for (List<T> list = this; !list.isEmpty(); list = list.tail(), index++) {
if (index >= from && Objects.equals(list.head(), element)) {
return index;
}
}
return -1;
}
@Override
default List<T> init() {
if (isEmpty()) {
throw new UnsupportedOperationException("init of empty list");
} else {
return dropRight(1);
}
}
@Override
default Option<List<T>> initOption() {
return isEmpty() ? Option.none() : Option.some(init());
}
@Override
int length();
@Override
default List<T> insert(int index, T element) {
if (index < 0) {
throw new IndexOutOfBoundsException("insert(" + index + ", e)");
}
List<T> preceding = Nil.instance();
List<T> tail = this;
for (int i = index; i > 0; i--, tail = tail.tail()) {
if (tail.isEmpty()) {
throw new IndexOutOfBoundsException("insert(" + index + ", e) on List of length " + length());
}
preceding = preceding.prepend(tail.head());
}
List<T> result = tail.prepend(element);
for (T next : preceding) {
result = result.prepend(next);
}
return result;
}
@Override
default List<T> insertAll(int index, Iterable<? extends T> elements) {
Objects.requireNonNull(elements, "elements is null");
if (index < 0) {
throw new IndexOutOfBoundsException("insertAll(" + index + ", elements)");
}
List<T> preceding = Nil.instance();
List<T> tail = this;
for (int i = index; i > 0; i--, tail = tail.tail()) {
if (tail.isEmpty()) {
throw new IndexOutOfBoundsException("insertAll(" + index + ", elements) on List of length " + length());
}
preceding = preceding.prepend(tail.head());
}
List<T> result = tail.prependAll(elements);
for (T next : preceding) {
result = result.prepend(next);
}
return result;
}
@Override
default List<T> intersperse(T element) {
return ofAll(iterator().intersperse(element));
}
@Override
default boolean isTraversableAgain() {
return true;
}
@Override
default int lastIndexOf(T element, int end) {
int result = -1, index = 0;
for (List<T> list = this; index <= end && !list.isEmpty(); list = list.tail(), index++) {
if (Objects.equals(list.head(), element)) {
result = index;
}
}
return result;
}
@Override
default <U> List<U> map(Function<? super T, ? extends U> mapper) {
Objects.requireNonNull(mapper, "mapper is null");
List<U> list = empty();
for (T t : this) {
list = list.prepend(mapper.apply(t));
}
return list.reverse();
}
@Override
default List<T> orElse(Iterable<? extends T> other) {
return isEmpty() ? ofAll(other) : this;
}
@Override
default List<T> orElse(Supplier<? extends Iterable<? extends T>> supplier) {
return isEmpty() ? ofAll(supplier.get()) : this;
}
@Override
default List<T> padTo(int length, T element) {
final int actualLength = length();
if (length <= actualLength) {
return this;
} else {
return appendAll(Iterator.continually(element).take(length - actualLength));
}
}
@Override
default List<T> leftPadTo(int length, T element) {
final int actualLength = length();
if (length <= actualLength) {
return this;
} else {
return prependAll(Iterator.continually(element).take(length - actualLength));
}
}
@Override
default List<T> patch(int from, Iterable<? extends T> that, int replaced) {
from = from < 0 ? 0 : from;
replaced = replaced < 0 ? 0 : replaced;
List<T> result = take(from).appendAll(that);
from += replaced;
result = result.appendAll(drop(from));
return result;
}
@Override
default Tuple2<List<T>, List<T>> partition(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate, "predicate is null");
List<T> left = empty(), right = empty();
for (T t : this) {
if (predicate.test(t)) {
left = left.prepend(t);
} else {
right = right.prepend(t);
}
}
return Tuple.of(left.reverse(), right.reverse());
}
/**
* Returns the head element without modifying the List.
*
* @return the first element
* @throws java.util.NoSuchElementException if this List is empty
*/
default T peek() {
if (isEmpty()) {
throw new NoSuchElementException("peek of empty list");
}
return head();
}
/**
* Returns the head element without modifying the List.
*
* @return {@code None} if this List is empty, otherwise a {@code Some} containing the head element
*/
default Option<T> peekOption() {
return isEmpty() ? Option.none() : Option.some(head());
}
/**
* Performs an action on the head element of this {@code List}.
*
* @param action A {@code Consumer}
* @return this {@code List}
*/
@Override
default List<T> peek(Consumer<? super T> action) {
Objects.requireNonNull(action, "action is null");
if (!isEmpty()) {
action.accept(head());
}
return this;
}
@Override
default List<List<T>> permutations() {
if (isEmpty()) {
return Nil.instance();
} else {
final List<T> tail = tail();
if (tail.isEmpty()) {
return of(this);
} else {
final List<List<T>> zero = Nil.instance();
return distinct().foldLeft(zero, (xs, x) -> {
final Function<List<T>, List<T>> prepend = l -> l.prepend(x);
return xs.appendAll(remove(x).permutations().map(prepend));
});
}
}
}
/**
* Removes the head element from this List.
*
* @return the elements of this List without the head element
* @throws java.util.NoSuchElementException if this List is empty
*/
default List<T> pop() {
if (isEmpty()) {
throw new NoSuchElementException("pop of empty list");
}
return tail();
}
/**
* Removes the head element from this List.
*
* @return {@code None} if this List is empty, otherwise a {@code Some} containing the elements of this List without the head element
*/
default Option<List<T>> popOption() {
return isEmpty() ? Option.none() : Option.some(pop());
}
/**
* Removes the head element from this List.
*
* @return a tuple containing the head element and the remaining elements of this List
* @throws java.util.NoSuchElementException if this List is empty
*/
default Tuple2<T, List<T>> pop2() {
if (isEmpty()) {
throw new NoSuchElementException("pop2 of empty list");
}
return Tuple.of(head(), tail());
}
/**
* Removes the head element from this List.
*
* @return {@code None} if this List is empty, otherwise {@code Some} {@code Tuple} containing the head element and the remaining elements of this List
*/
default Option<Tuple2<T, List<T>>> pop2Option() {
return isEmpty() ? Option.none() : Option.some(Tuple.of(head(), pop()));
}
@Override
default List<T> prepend(T element) {
return new Cons<>(element, this);
}
@Override
default List<T> prependAll(Iterable<? extends T> elements) {
Objects.requireNonNull(elements, "elements is null");
return isEmpty() ? ofAll(elements) : ofAll(elements).reverse().foldLeft(this, List::prepend);
}
/**
* Pushes a new element on top of this List.
*
* @param element The new element
* @return a new {@code List} instance, containing the new element on top of this List
*/
default List<T> push(T element) {
return new Cons<>(element, this);
}
/**
* Pushes the given elements on top of this List. A List has LIFO order, i.e. the last of the given elements is
* the first which will be retrieved.
*
* @param elements Elements, may be empty
* @return a new {@code List} instance, containing the new elements on top of this List
* @throws NullPointerException if elements is null
*/
@SuppressWarnings("unchecked")
default List<T> push(T... elements) {
Objects.requireNonNull(elements, "elements is null");
List<T> result = this;
for (T element : elements) {
result = result.prepend(element);
}
return result;
}
/**
* Pushes the given elements on top of this List. A List has LIFO order, i.e. the last of the given elements is
* the first which will be retrieved.
*
* @param elements An Iterable of elements, may be empty
* @return a new {@code List} instance, containing the new elements on top of this List
* @throws NullPointerException if elements is null
*/
default List<T> pushAll(Iterable<T> elements) {
Objects.requireNonNull(elements, "elements is null");
List<T> result = this;
for (T element : elements) {
result = result.prepend(element);
}
return result;
}
@Override
default List<T> remove(T element) {
final Deque<T> preceding = new ArrayDeque<>(size());
List<T> result = this;
boolean found = false;
while (!found && !result.isEmpty()) {
final T head = result.head();
if (Objects.equals(head, element)) {
found = true;
} else {
preceding.addFirst(head);
}
result = result.tail();
}
if (!found) {
return this;
}
for (T next : preceding) {
result = result.prepend(next);
}
return result;
}
@Override
default List<T> removeFirst(Predicate<T> predicate) {
Objects.requireNonNull(predicate, "predicate is null");
List<T> init = empty();
List<T> tail = this;
while (!tail.isEmpty() && !predicate.test(tail.head())) {
init = init.prepend(tail.head());
tail = tail.tail();
}
if (tail.isEmpty()) {
return this;
} else {
return init.foldLeft(tail.tail(), List::prepend);
}
}
@Override
default List<T> removeLast(Predicate<T> predicate) {
Objects.requireNonNull(predicate, "predicate is null");
final List<T> removedAndReversed = reverse().removeFirst(predicate);
return removedAndReversed.length() == length() ? this : removedAndReversed.reverse();
}
@Override
default List<T> removeAt(int index) {
if (index < 0) {
throw new IndexOutOfBoundsException("removeAt(" + index + ")");
}
if (isEmpty()) {
throw new IndexOutOfBoundsException("removeAt(" + index + ") on Nil");
}
List<T> init = Nil.instance();
List<T> tail = this;
while (index > 0 && !tail.isEmpty()) {
init = init.prepend(tail.head());
tail = tail.tail();
index--;
}
if (index > 0) {
throw new IndexOutOfBoundsException("removeAt() on Nil");
}
return init.reverse().appendAll(tail.tail());
}
@Override
default List<T> removeAll(T element) {
return Collections.removeAll(this, element);
}
@Override
default List<T> removeAll(Iterable<? extends T> elements) {
return Collections.removeAll(this, elements);
}
@Override
default List<T> removeAll(Predicate<? super T> predicate) {
return Collections.removeAll(this, predicate);
}
@Override
default List<T> replace(T currentElement, T newElement) {
List<T> preceding = Nil.instance();
List<T> tail = this;
while (!tail.isEmpty() && !Objects.equals(tail.head(), currentElement)) {
preceding = preceding.prepend(tail.head());
tail = tail.tail();
}
if (tail.isEmpty()) {
return this;
}
// skip the current head element because it is replaced
List<T> result = tail.tail().prepend(newElement);
for (T next : preceding) {
result = result.prepend(next);
}
return result;
}
@Override
default List<T> replaceAll(T currentElement, T newElement) {
List<T> result = Nil.instance();
boolean changed = false;
for (List<T> list = this; !list.isEmpty(); list = list.tail()) {
final T head = list.head();
if (Objects.equals(head, currentElement)) {
result = result.prepend(newElement);
changed = true;
} else {
result = result.prepend(head);
}
}
return changed ? result.reverse() : this;
}
@Override
default List<T> retainAll(Iterable<? extends T> elements) {
return Collections.retainAll(this, elements);
}
@Override
default List<T> reverse() {
return (length() <= 1) ? this : foldLeft(empty(), List::prepend);
}
@Override
default List<T> scan(T zero, BiFunction<? super T, ? super T, ? extends T> operation) {
return scanLeft(zero, operation);
}
@Override
default <U> List<U> scanLeft(U zero, BiFunction<? super U, ? super T, ? extends U> operation) {
return Collections.scanLeft(this, zero, operation, Iterator::toList);
}
@Override
default <U> List<U> scanRight(U zero, BiFunction<? super T, ? super U, ? extends U> operation) {
return Collections.scanRight(this, zero, operation, Iterator::toList);
}
@Override
default List<T> shuffle() {
return Collections.shuffle(this, List::ofAll);
}
@Override
default List<T> slice(int beginIndex, int endIndex) {
if (beginIndex >= endIndex || beginIndex >= length() || isEmpty()) {
return empty();
} else {
List<T> result = Nil.instance();
List<T> list = this;
final long lowerBound = Math.max(beginIndex, 0);
final long upperBound = Math.min(endIndex, length());
for (int i = 0; i < upperBound; i++) {
if (i >= lowerBound) {
result = result.prepend(list.head());
}
list = list.tail();
}
return result.reverse();
}
}
@Override
default Iterator<List<T>> slideBy(Function<? super T, ?> classifier) {
return iterator().slideBy(classifier).map(List::ofAll);
}
@Override
default Iterator<List<T>> sliding(int size) {
return sliding(size, 1);
}
@Override
default Iterator<List<T>> sliding(int size, int step) {
return iterator().sliding(size, step).map(List::ofAll);
}
@Override
default List<T> sorted() {
return isEmpty() ? this : toJavaStream().sorted().collect(collector());
}
@Override
default List<T> sorted(Comparator<? super T> comparator) {
Objects.requireNonNull(comparator, "comparator is null");
return isEmpty() ? this : toJavaStream().sorted(comparator).collect(collector());
}
@Override
default <U extends Comparable<? super U>> List<T> sortBy(Function<? super T, ? extends U> mapper) {
return sortBy(U::compareTo, mapper);
}
@Override
default <U> List<T> sortBy(Comparator<? super U> comparator, Function<? super T, ? extends U> mapper) {
final Function<? super T, ? extends U> domain = Function1.of(mapper::apply).memoized();
return toJavaStream()
.sorted((e1, e2) -> comparator.compare(domain.apply(e1), domain.apply(e2)))
.collect(collector());
}
@Override
default Tuple2<List<T>, List<T>> span(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate, "predicate is null");
final Tuple2<Iterator<T>, Iterator<T>> itt = iterator().span(predicate);
return Tuple.of(ofAll(itt._1), ofAll(itt._2));
}
@Override
default Tuple2<List<T>, List<T>> splitAt(int n) {
if (isEmpty()) {
return Tuple.of(empty(), empty());
} else {
List<T> init = Nil.instance();
List<T> tail = this;
while (n > 0 && !tail.isEmpty()) {
init = init.prepend(tail.head());
tail = tail.tail();
n--;
}
return Tuple.of(init.reverse(), tail);
}
}
@Override
default Tuple2<List<T>, List<T>> splitAt(Predicate<? super T> predicate) {
if (isEmpty()) {
return Tuple.of(empty(), empty());
} else {
final Tuple2<List<T>, List<T>> t = SplitAt.splitByPredicateReversed(this, predicate);
if (t._2.isEmpty()) {
return Tuple.of(this, empty());
} else {
return Tuple.of(t._1.reverse(), t._2);
}
}
}
@Override
default Tuple2<List<T>, List<T>> splitAtInclusive(Predicate<? super T> predicate) {
if (isEmpty()) {
return Tuple.of(empty(), empty());
} else {
final Tuple2<List<T>, List<T>> t = SplitAt.splitByPredicateReversed(this, predicate);
if (t._2.isEmpty() || t._2.tail().isEmpty()) {
return Tuple.of(this, empty());
} else {
return Tuple.of(t._1.prepend(t._2.head()).reverse(), t._2.tail());
}
}
}
@Override
default String stringPrefix() {
return "List";
}
@Override
default List<T> subSequence(int beginIndex) {
if (beginIndex < 0 || beginIndex > length()) {
throw new IndexOutOfBoundsException("subSequence(" + beginIndex + ")");
} else {
return drop(beginIndex);
}
}
@Override
default List<T> subSequence(int beginIndex, int endIndex) {
Collections.subSequenceRangeCheck(beginIndex, endIndex, length());
if (beginIndex == endIndex) {
return empty();
} else if (beginIndex == 0 && endIndex == length()) {
return this;
} else {
List<T> result = Nil.instance();
List<T> list = this;
for (int i = 0; i < endIndex; i++, list = list.tail()) {
if (i >= beginIndex) {
result = result.prepend(list.head());
}
}
return result.reverse();
}
}
@Override
List<T> tail();
@Override
default Option<List<T>> tailOption() {
return isEmpty() ? Option.none() : Option.some(tail());
}
@Override
default List<T> take(int n) {
if (n <= 0) {
return empty();
}
if (n >= length()) {
return this;
}
List<T> result = Nil.instance();
List<T> list = this;
for (int i = 0; i < n; i++, list = list.tail()) {
result = result.prepend(list.head());
}
return result.reverse();
}
@Override
default List<T> takeRight(int n) {
if (n <= 0) {
return empty();
}
if (n >= length()) {
return this;
}
return reverse().take(n).reverse();
}
@Override
default List<T> takeUntil(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate, "predicate is null");
return takeWhile(predicate.negate());
}
@Override
default List<T> takeWhile(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate, "predicate is null");
List<T> result = Nil.instance();
for (List<T> list = this; !list.isEmpty() && predicate.test(list.head()); list = list.tail()) {
result = result.prepend(list.head());
}
return result.length() == length() ? this : result.reverse();
}
/**
* Transforms this {@code List}.
*
* @param f A transformation
* @param <U> Type of transformation result
* @return An instance of type {@code U}
* @throws NullPointerException if {@code f} is null
*/
default <U> U transform(Function<? super List<T>, ? extends U> f) {
Objects.requireNonNull(f, "f is null");
return f.apply(this);
}
@Override
default <T1, T2> Tuple2<List<T1>, List<T2>> unzip(
Function<? super T, Tuple2<? extends T1, ? extends T2>> unzipper) {
Objects.requireNonNull(unzipper, "unzipper is null");
List<T1> xs = Nil.instance();
List<T2> ys = Nil.instance();
for (T element : this) {
final Tuple2<? extends T1, ? extends T2> t = unzipper.apply(element);
xs = xs.prepend(t._1);
ys = ys.prepend(t._2);
}
return Tuple.of(xs.reverse(), ys.reverse());
}
@Override
default <T1, T2, T3> Tuple3<List<T1>, List<T2>, List<T3>> unzip3(
Function<? super T, Tuple3<? extends T1, ? extends T2, ? extends T3>> unzipper) {
Objects.requireNonNull(unzipper, "unzipper is null");
List<T1> xs = Nil.instance();
List<T2> ys = Nil.instance();
List<T3> zs = Nil.instance();
for (T element : this) {
final Tuple3<? extends T1, ? extends T2, ? extends T3> t = unzipper.apply(element);
xs = xs.prepend(t._1);
ys = ys.prepend(t._2);
zs = zs.prepend(t._3);
}
return Tuple.of(xs.reverse(), ys.reverse(), zs.reverse());
}
@Override
default List<T> update(int index, T element) {
if (isEmpty()) {
throw new IndexOutOfBoundsException("update(" + index + ", e) on Nil");
}
if (index < 0) {
throw new IndexOutOfBoundsException("update(" + index + ", e)");
}
List<T> preceding = Nil.instance();
List<T> tail = this;
for (int i = index; i > 0; i--, tail = tail.tail()) {
if (tail.isEmpty()) {
throw new IndexOutOfBoundsException("update(" + index + ", e) on List of length " + length());
}
preceding = preceding.prepend(tail.head());
}
if (tail.isEmpty()) {
throw new IndexOutOfBoundsException("update(" + index + ", e) on List of length " + length());
}
// skip the current head element because it is replaced
List<T> result = tail.tail().prepend(element);
for (T next : preceding) {
result = result.prepend(next);
}
return result;
}
@Override
default List<T> update(int index, Function<? super T, ? extends T> updater) {
Objects.requireNonNull(updater, "updater is null");
return update(index, updater.apply(get(index)));
}
@Override
default <U> List<Tuple2<T, U>> zip(Iterable<? extends U> that) {
return zipWith(that, Tuple::of);
}
@Override
default <U, R> List<R> zipWith(Iterable<? extends U> that, BiFunction<? super T, ? super U, ? extends R> mapper) {
Objects.requireNonNull(that, "that is null");
Objects.requireNonNull(mapper, "mapper is null");
return ofAll(iterator().zipWith(that, mapper));
}
@Override
default <U> List<Tuple2<T, U>> zipAll(Iterable<? extends U> that, T thisElem, U thatElem) {
Objects.requireNonNull(that, "that is null");
return ofAll(iterator().zipAll(that, thisElem, thatElem));
}
@Override
default List<Tuple2<T, Integer>> zipWithIndex() {
return zipWithIndex(Tuple::of);
}
@Override
default <U> List<U> zipWithIndex(BiFunction<? super T, ? super Integer, ? extends U> mapper) {
Objects.requireNonNull(mapper, "mapper is null");
return ofAll(iterator().zipWithIndex(mapper));
}
/**
* Representation of the singleton empty {@code List}.
*
* @param <T> Component type of the List.
*/
final class Nil<T> implements List<T>, Serializable {
private static final long serialVersionUID = 1L;
private static final Nil<?> INSTANCE = new Nil<>();
// hidden
private Nil() {
}
/**
* Returns the singleton instance of the linked list.
*
* @param <T> Component type of the List
* @return the singleton instance of the linked list.
*/
@SuppressWarnings("unchecked")
public static <T> Nil<T> instance() {
return (Nil<T>) INSTANCE;
}
@Override
public T head() {
throw new NoSuchElementException("head of empty list");
}
@Override
public int length() {
return 0;
}
@Override
public List<T> tail() {
throw new UnsupportedOperationException("tail of empty list");
}
@Override
public boolean isEmpty() {
return true;
}
@Override
public boolean equals(Object o) {
return Collections.equals(this, o);
}
@Override
public int hashCode() {
return Collections.hashOrdered(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 List}, consisting of a {@code head} and a {@code tail}.
*
* @param <T> Component type of the List.
*/
// DEV NOTE: class declared final because of serialization proxy pattern (see Effective Java, 2nd ed., p. 315)
final class Cons<T> implements List<T>, Serializable {
private static final long serialVersionUID = 1L;
private final T head;
private final List<T> tail;
private final int length;
/**
* Creates a List consisting of a head value and a trailing List.
*
* @param head The head
* @param tail The tail
*/
private Cons(T head, List<T> tail) {
this.head = head;
this.tail = tail;
this.length = 1 + tail.length();
}
@Override
public T head() {
return head;
}
@Override
public int length() {
return length;
}
@Override
public List<T> tail() {
return tail;
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public boolean equals(Object o) {
return Collections.equals(this, o);
}
@Override
public int hashCode() {
return Collections.hashOrdered(this);
}
@Override
public String toString() {
return mkString(stringPrefix() + "(", ", ", ")");
}
/**
* {@code writeReplace} method for the serialization proxy pattern.
* <p>
* The presence of this method causes the serialization system to emit a SerializationProxy instance instead of
* an instance of the enclosing class.
*
* @return A SerializationProxy for this enclosing class.
*/
@GwtIncompatible("The Java serialization protocol is explicitly not supported")
private Object writeReplace() {
return new SerializationProxy<>(this);
}
/**
* {@code readObject} method for the serialization proxy pattern.
* <p>
* Guarantees that the serialization system will never generate a serialized instance of the enclosing class.
*
* @param stream An object serialization stream.
* @throws java.io.InvalidObjectException This method will throw with the message "Proxy required".
*/
@GwtIncompatible("The Java serialization protocol is explicitly not supported")
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 Lists with final
* instance fields.
*
* @param <T> The component type of the underlying list.
*/
// 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.
@GwtIncompatible("The Java serialization protocol is explicitly not supported")
private static final class SerializationProxy<T> implements Serializable {
private static final long serialVersionUID = 1L;
// the instance to be serialized/deserialized
private transient Cons<T> list;
/**
* Constructor for the case of serialization, called by {@link Cons#writeReplace()}.
* <p/>
* The constructor of a SerializationProxy takes an argument that concisely represents the logical state of
* an instance of the enclosing class.
*
* @param list a Cons
*/
SerializationProxy(Cons<T> list) {
this.list = list;
}
/**
* 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(list.length());
for (List<T> l = list; !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 list 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");
}
List<T> temp = Nil.instance();
for (int i = 0; i < size; i++) {
@SuppressWarnings("unchecked")
final T element = (T) s.readObject();
temp = temp.prepend(element);
}
list = (Cons<T>) temp.reverse();
}
/**
* {@code readResolve} method for the serialization proxy pattern.
* <p>
* 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 list;
}
}
}
}
interface ListModule {
interface Combinations {
static <T> List<List<T>> apply(List<T> elements, int k) {
if (k == 0) {
return List.of(List.empty());
} else {
return elements.zipWithIndex().flatMap(
t -> apply(elements.drop(t._2 + 1), (k - 1)).map(c -> c.prepend(t._1))
);
}
}
}
interface SplitAt {
static <T> Tuple2<List<T>, List<T>> splitByPredicateReversed(List<T> source, Predicate<? super T> predicate) {
Objects.requireNonNull(predicate, "predicate is null");
List<T> init = Nil.instance();
List<T> tail = source;
while (!tail.isEmpty() && !predicate.test(tail.head())) {
init = init.prepend(tail.head());
tail = tail.tail();
}
return Tuple.of(init, tail);
}
}
}