javaslang.collection.List Maven / Gradle / Ivy
Show all versions of javaslang Show documentation
/* / \____ _ _ ____ ______ / \ ____ __ _ _____
* / / \/ \ / \/ \ / /\__\/ // \/ \ / / _ \ Javaslang
* _/ / /\ \ \/ / /\ \\__\\ \ // /\ \ /\\/ \__/ / Copyright 2014-now Daniel Dietrich
* /___/\_/ \_/\____/\_/ \_/\__\/__/___\_/ \_// \__/_____/ Licensed under the Apache License, Version 2.0
*/
package javaslang.collection;
import javaslang.*;
import javaslang.collection.List.Nil;
import javaslang.collection.ListModule.Combinations;
import javaslang.collection.ListModule.SplitAt;
import javaslang.control.None;
import javaslang.control.Option;
import javaslang.control.Some;
import java.io.*;
import java.util.*;
import java.util.function.*;
import java.util.stream.Collector;
/**
* An immutable {@code List} is an eager sequence of elements. Its immutability makes it suitable for concurrent programming.
*
* A {@code List} is composed of a {@code head} element and a {@code tail} {@code List}.
*
* There are two implementations of the {@code List} interface:
*
* - {@link Nil}, which represents the empty {@code List}.
* - {@link Cons}, which represents a {@code List} containing one or more elements.
*
* Methods to obtain a {@code List}:
*
*
* // 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(java.lang.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
*
*
*
* Note: A {@code List} is primary a {@code Seq} and extends {@code Stack} for technical reasons (so {@code Stack} does not need to wrap {@code List}).
*
*
* Factory method applications:
*
*
*
* 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(new int[] {1, 2, 3});
* List<List<Integer>> s4 = List.ofAll(List.of(1, 2, 3));
*
* List<Integer> s5 = List.ofAll(new int[] {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});
*
*
*
* Example: Converting a String to digits
*
*
*
* // = List(1, 2, 3)
* List.of("123".toCharArray()).map(c -> Character.digit(c, 10))
*
*
*
* See Okasaki, Chris: Purely Functional Data Structures (p. 7 ff.). Cambridge, 2003.
*
* @param Component type of the List
* @author Daniel Dietrich
* @since 1.1.0
*/
public interface List extends LinearSeq, Stack {
/**
* 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.List}.
*
* @param Component type of the List.
* @return A javaslang.collection.List Collector.
*/
static Collector, List> collector() {
final Supplier> supplier = ArrayList::new;
final BiConsumer, T> accumulator = ArrayList::add;
final BinaryOperator> combiner = (left, right) -> {
left.addAll(right);
return left;
};
final Function, List> finisher = List::ofAll;
return Collector.of(supplier, accumulator, combiner, finisher);
}
/**
* Returns the single instance of Nil. Convenience method for {@code Nil.instance()} .
*
* 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 Component type of Nil, determined by type inference in the particular context.
* @return The empty list.
*/
static List empty() {
return Nil.instance();
}
/**
* Returns a singleton {@code List}, i.e. a {@code List} of one element.
*
* @param element An element.
* @param The component type
* @return A new List instance containing the given element
*/
static List of(T element) {
return new Cons<>(element, Nil.instance());
}
/**
* Creates a List of the given elements.
*
*
* 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()))))
*
*
*
* @param 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 List of(T... elements) {
Objects.requireNonNull(elements, "elements is null");
List 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.
*
* The resulting list has the same iteration order as the given iterable of elements
* if the iteration order of the elements is stable.
*
* @param Component type of the List.
* @param elements An java.lang.Iterable of elements.
* @return A list containing the given elements in the same order.
* @throws NullPointerException if {@code elements} is null
*/
@SuppressWarnings("unchecked")
static List ofAll(java.lang.Iterable extends T> elements) {
Objects.requireNonNull(elements, "elements is null");
if (elements instanceof List) {
return (List) elements;
} else if (elements instanceof java.util.List) {
List result = Nil.instance();
final java.util.List list = (java.util.List) elements;
final ListIterator iterator = list.listIterator(list.size());
while (iterator.hasPrevious()) {
result = result.prepend(iterator.previous());
}
return result;
} else if (elements instanceof NavigableSet) {
List result = Nil.instance();
final java.util.Iterator iterator = ((NavigableSet) elements).descendingIterator();
while (iterator.hasNext()) {
result = result.prepend(iterator.next());
}
return result;
} else {
List result = Nil.instance();
for (T element : elements) {
result = result.prepend(element);
}
return result.reverse();
}
}
/**
* Creates a List based on the elements of a boolean array.
*
* @param array a boolean array
* @return A new List of Boolean values
*/
static List ofAll(boolean[] array) {
Objects.requireNonNull(array, "array is null");
return List.ofAll(Iterator.ofAll(array));
}
/**
* Creates a List based on the elements of a byte array.
*
* @param array a byte array
* @return A new List of Byte values
*/
static List ofAll(byte[] array) {
Objects.requireNonNull(array, "array is null");
return List.ofAll(Iterator.ofAll(array));
}
/**
* Creates a List based on the elements of a char array.
*
* @param array a char array
* @return A new List of Character values
*/
static List ofAll(char[] array) {
Objects.requireNonNull(array, "array is null");
return List.ofAll(Iterator.ofAll(array));
}
/**
* Creates a List based on the elements of a double array.
*
* @param array a double array
* @return A new List of Double values
*/
static List ofAll(double[] array) {
Objects.requireNonNull(array, "array is null");
return List.ofAll(Iterator.ofAll(array));
}
/**
* Creates a List based on the elements of a float array.
*
* @param array a float array
* @return A new List of Float values
*/
static List ofAll(float[] array) {
Objects.requireNonNull(array, "array is null");
return List.ofAll(Iterator.ofAll(array));
}
/**
* Creates a List based on the elements of an int array.
*
* @param array an int array
* @return A new List of Integer values
*/
static List ofAll(int[] array) {
Objects.requireNonNull(array, "array is null");
return List.ofAll(Iterator.ofAll(array));
}
/**
* Creates a List based on the elements of a long array.
*
* @param array a long array
* @return A new List of Long values
*/
static List ofAll(long[] array) {
Objects.requireNonNull(array, "array is null");
return List.ofAll(Iterator.ofAll(array));
}
/**
* Creates a List based on the elements of a short array.
*
* @param array a short array
* @return A new List of Short values
*/
static List ofAll(short[] array) {
Objects.requireNonNull(array, "array is null");
return List.ofAll(Iterator.ofAll(array));
}
static List range(char from, char toExclusive) {
return List.ofAll(Iterator.range(from, toExclusive));
}
static List rangeBy(char from, char toExclusive, int step) {
return List.ofAll(Iterator.rangeBy(from, toExclusive, step));
}
static List rangeBy(double from, double toExclusive, double step) {
return List.ofAll(Iterator.rangeBy(from, toExclusive, step));
}
/**
* Creates a List of int numbers starting from {@code from}, extending to {@code toExclusive - 1}.
*
* Examples:
*
*
* List.range(0, 0) // = List()
* List.range(2, 0) // = List()
* List.range(-2, 2) // = List(-2, -1, 0, 1)
*
*
*
* @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 range(int from, int toExclusive) {
return List.ofAll(Iterator.range(from, toExclusive));
}
/**
* Creates a List of int numbers starting from {@code from}, extending to {@code toExclusive - 1},
* with {@code step}.
*
* Examples:
*
*
* 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()
*
*
*
* @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
* {@code from >= toInclusive} and {@code step > 0} or
* {@code from <= toInclusive} and {@code step < 0}
* @throws IllegalArgumentException if {@code step} is zero
*/
static List rangeBy(int from, int toExclusive, int step) {
return List.ofAll(Iterator.rangeBy(from, toExclusive, step));
}
/**
* Creates a List of long numbers starting from {@code from}, extending to {@code toExclusive - 1}.
*
* Examples:
*
*
* List.range(0L, 0L) // = List()
* List.range(2L, 0L) // = List()
* List.range(-2L, 2L) // = List(-2L, -1L, 0L, 1L)
*
*
*
* @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 range(long from, long toExclusive) {
return List.ofAll(Iterator.range(from, toExclusive));
}
/**
* Creates a List of long numbers starting from {@code from}, extending to {@code toExclusive - 1},
* with {@code step}.
*
* Examples:
*
*
* 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()
*
*
*
* @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
* {@code from >= toInclusive} and {@code step > 0} or
* {@code from <= toInclusive} and {@code step < 0}
* @throws IllegalArgumentException if {@code step} is zero
*/
static List rangeBy(long from, long toExclusive, long step) {
return List.ofAll(Iterator.rangeBy(from, toExclusive, step));
}
static List rangeClosed(char from, char toInclusive) {
return List.ofAll(Iterator.rangeClosed(from, toInclusive));
}
static List rangeClosedBy(char from, char toInclusive, int step) {
return List.ofAll(Iterator.rangeClosedBy(from, toInclusive, step));
}
static List rangeClosedBy(double from, double toInclusive, double step) {
return List.ofAll(Iterator.rangeClosedBy(from, toInclusive, step));
}
/**
* Creates a List of int numbers starting from {@code from}, extending to {@code toInclusive}.
*
* Examples:
*
*
* List.rangeClosed(0, 0) // = List(0)
* List.rangeClosed(2, 0) // = List()
* List.rangeClosed(-2, 2) // = List(-2, -1, 0, 1, 2)
*
*
*
* @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 rangeClosed(int from, int toInclusive) {
return List.ofAll(Iterator.rangeClosed(from, toInclusive));
}
/**
* Creates a List of int numbers starting from {@code from}, extending to {@code toInclusive},
* with {@code step}.
*
* Examples:
*
*
* 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()
*
*
*
* @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
* {@code from > toInclusive} and {@code step > 0} or
* {@code from < toInclusive} and {@code step < 0}
* @throws IllegalArgumentException if {@code step} is zero
*/
static List rangeClosedBy(int from, int toInclusive, int step) {
return List.ofAll(Iterator.rangeClosedBy(from, toInclusive, step));
}
/**
* Creates a List of long numbers starting from {@code from}, extending to {@code toInclusive}.
*
* Examples:
*
*
* List.rangeClosed(0L, 0L) // = List(0L)
* List.rangeClosed(2L, 0L) // = List()
* List.rangeClosed(-2L, 2L) // = List(-2L, -1L, 0L, 1L, 2L)
*
*
*
* @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 rangeClosed(long from, long toInclusive) {
return List.ofAll(Iterator.rangeClosed(from, toInclusive));
}
/**
* Creates a List of long numbers starting from {@code from}, extending to {@code toInclusive},
* with {@code step}.
*
* Examples:
*
*
* 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()
*
*
*
* @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
* {@code from > toInclusive} and {@code step > 0} or
* {@code from < toInclusive} and {@code step < 0}
* @throws IllegalArgumentException if {@code step} is zero
*/
static List rangeClosedBy(long from, long toInclusive, long step) {
return List.ofAll(Iterator.rangeClosedBy(from, toInclusive, step));
}
@Override
default List append(T element) {
return foldRight(List.of(element), (x, xs) -> xs.prepend(x));
}
@Override
default List appendAll(java.lang.Iterable extends T> elements) {
Objects.requireNonNull(elements, "elements is null");
return foldRight(List.ofAll(elements), (x, xs) -> xs.prepend(x));
}
@Override
default List clear() {
return Nil.instance();
}
@Override
default List> combinations() {
return List.rangeClosed(0, length()).map(this::combinations).flatMap(Function.identity());
}
@Override
default List> combinations(int k) {
return Combinations.apply(this, Math.max(k, 0));
}
@Override
default List> crossProduct() {
return crossProduct(this);
}
@Override
default List> crossProduct(int power) {
return Collections.crossProduct(this, power).map(List::ofAll).toList();
}
@Override
default List> crossProduct(java.lang.Iterable extends U> that) {
Objects.requireNonNull(that, "that is null");
final List other = unit(that);
return flatMap(a -> other.map((Function>) b -> Tuple.of(a, b)));
}
@Override
default List distinct() {
return distinctBy(Function.identity());
}
@Override
default List distinctBy(Comparator super T> comparator) {
Objects.requireNonNull(comparator, "comparator is null");
final java.util.Set seen = new java.util.TreeSet<>(comparator);
return filter(seen::add);
}
@Override
default List distinctBy(Function super T, ? extends U> keyExtractor) {
Objects.requireNonNull(keyExtractor, "keyExtractor is null");
final java.util.Set seen = new java.util.HashSet<>();
return filter(t -> seen.add(keyExtractor.apply(t)));
}
@Override
default List drop(int n) {
List list = this;
for (int i = n; i > 0 && !list.isEmpty(); i--) {
list = list.tail();
}
return list;
}
@Override
default List dropRight(int n) {
if (n <= 0) {
return this;
}
if (n >= length()) {
return empty();
}
return List.ofAll(iterator().dropRight(n));
}
@Override
default List dropUntil(Predicate super T> predicate) {
Objects.requireNonNull(predicate, "predicate is null");
return dropWhile(predicate.negate());
}
@Override
default List dropWhile(Predicate super T> predicate) {
Objects.requireNonNull(predicate, "predicate is null");
List list = this;
while (!list.isEmpty() && predicate.test(list.head())) {
list = list.tail();
}
return list;
}
@Override
default List filter(Predicate super T> predicate) {
Objects.requireNonNull(predicate, "predicate is null");
final List filtered = foldLeft(List. empty(), (xs, x) -> predicate.test(x) ? xs.prepend(x) : xs);
return this.length() == filtered.length() ? this : filtered.reverse();
}
@Override
default List flatMap(Function super T, ? extends java.lang.Iterable extends U>> mapper) {
Objects.requireNonNull(mapper, "mapper is null");
if (isEmpty()) {
return empty();
} else {
List list = empty();
for (T t : this) {
for (U u : mapper.apply(t)) {
list = list.prepend(u);
}
}
return list.reverse();
}
}
@Override
default void forEach(Consumer super T> action) {
Objects.requireNonNull(action, "action is null");
Stack.super.forEach(action);
}
@Override
default boolean forAll(Predicate super T> predicate) {
Objects.requireNonNull(predicate, "predicate is null");
return Stack.super.forAll(predicate);
}
@Override
default T get(int index) {
if (isEmpty()) {
throw new IndexOutOfBoundsException("get(" + index + ") on Nil");
}
if (index < 0) {
throw new IndexOutOfBoundsException("get(" + index + ")");
}
List list = this;
for (int i = index - 1; i >= 0; i--) {
list = list.tail();
if (list.isEmpty()) {
throw new IndexOutOfBoundsException(String.format("get(%s) on List of length %s", index, index - i));
}
}
return list.head();
}
@Override
default Map> groupBy(Function super T, ? extends C> classifier) {
Objects.requireNonNull(classifier, "classifier is null");
return iterator().groupBy(classifier).map((c, it) -> Tuple.of(c, List.ofAll(it)));
}
@Override
default Iterator> grouped(int size) {
return sliding(size, size);
}
@Override
default boolean hasDefiniteSize() {
return true;
}
@Override
default Option headOption() {
return isEmpty() ? None.instance() : new Some<>(head());
}
@Override
default int indexOf(T element, int from) {
int index = 0;
for (List list = this; !list.isEmpty(); list = list.tail(), index++) {
if (index >= from && Objects.equals(list.head(), element)) {
return index;
}
}
return -1;
}
@Override
default List init() {
if (isEmpty()) {
throw new UnsupportedOperationException("init of empty list");
} else {
return dropRight(1);
}
}
@Override
default Option> initOption() {
return isEmpty() ? None.instance() : new Some<>(init());
}
@Override
int length();
@Override
default List insert(int index, T element) {
if (index < 0) {
throw new IndexOutOfBoundsException("insert(" + index + ", e)");
}
List preceding = Nil.instance();
List 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 result = tail.prepend(element);
for (T next : preceding) {
result = result.prepend(next);
}
return result;
}
@Override
default List insertAll(int index, java.lang.Iterable extends T> elements) {
Objects.requireNonNull(elements, "elements is null");
if (index < 0) {
throw new IndexOutOfBoundsException("insertAll(" + index + ", elements)");
}
List preceding = Nil.instance();
List 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 result = tail.prependAll(elements);
for (T next : preceding) {
result = result.prepend(next);
}
return result;
}
@Override
default List intersperse(T element) {
return isEmpty()
? Nil.instance()
: foldRight(empty(), (x, xs) -> xs.isEmpty() ? xs.prepend(x) : xs.prepend(element).prepend(x));
}
@Override
default boolean isTraversableAgain() {
return true;
}
@Override
default int lastIndexOf(T element, int end) {
int result = -1, index = 0;
for (List list = this; index <= end && !list.isEmpty(); list = list.tail(), index++) {
if (Objects.equals(list.head(), element)) {
result = index;
}
}
return result;
}
@Override
default List map(Function super T, ? extends U> mapper) {
Objects.requireNonNull(mapper, "mapper is null");
List list = empty();
for (T t : this) {
list = list.prepend(mapper.apply(t));
}
return list.reverse();
}
@Override
default List padTo(int length, T element) {
if (length <= length()) {
return this;
} else {
return appendAll(Iterator.gen(() -> element).take(length - length()));
}
}
@Override
default List patch(int from, java.lang.Iterable extends T> that, int replaced) {
from = from < 0 ? 0 : from;
replaced = replaced < 0 ? 0 : replaced;
List result = take(from).appendAll(that);
from += replaced;
result = result.appendAll(drop(from));
return result;
}
@Override
default Tuple2, List> partition(Predicate super T> predicate) {
Objects.requireNonNull(predicate, "predicate is null");
final java.util.List left = new ArrayList<>(), right = new ArrayList<>();
for (T t : this) {
(predicate.test(t) ? left : right).add(t);
}
return Tuple.of(List.ofAll(left), List.ofAll(right));
}
@Override
default T peek() {
return head();
}
@Override
default Option peekOption() {
return isEmpty() ? None.instance() : new 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 peek(Consumer super T> action) {
Objects.requireNonNull(action, "action is null");
if (!isEmpty()) {
action.accept(head());
}
return this;
}
@Override
default List> permutations() {
if (isEmpty()) {
return Nil.instance();
} else {
final List tail = tail();
if (tail.isEmpty()) {
return List.of(this);
} else {
final List> zero = Nil.instance();
return distinct().foldLeft(zero, (xs, x) -> {
final Function, List> prepend = l -> l.prepend(x);
return xs.appendAll(remove(x).permutations().map(prepend));
});
}
}
}
@Override
default List pop() {
return tail();
}
@Override
default Option> popOption() {
return isEmpty() ? None.instance() : new Some<>(tail());
}
@Override
default Tuple2> pop2() {
return Tuple.of(head(), tail());
}
@Override
default Option>> pop2Option() {
return isEmpty() ? None.instance() : new Some<>(Tuple.of(head(), tail()));
}
@Override
default List prepend(T element) {
return new Cons<>(element, this);
}
@Override
default List prependAll(java.lang.Iterable extends T> elements) {
Objects.requireNonNull(elements, "elements is null");
return isEmpty() ? List.ofAll(elements) : List.ofAll(elements).reverse().foldLeft(this, List::prepend);
}
@Override
default List push(T element) {
return new Cons<>(element, this);
}
@SuppressWarnings("unchecked")
@Override
default List push(T... elements) {
Objects.requireNonNull(elements, "elements is null");
List result = Nil. instance();
for (T element : elements) {
result = result.prepend(element);
}
return result;
}
@Override
default List pushAll(java.lang.Iterable elements) {
Objects.requireNonNull(elements, "elements is null");
List result = Nil. instance();
for (T element : elements) {
result = result.prepend(element);
}
return result;
}
@Override
default List remove(T element) {
List preceding = Nil.instance();
List tail = this;
boolean found = false;
while (!found && !tail.isEmpty()) {
final T head = tail.head();
if (head.equals(element)) {
found = true;
} else {
preceding = preceding.prepend(head);
}
tail = tail.tail();
}
if (!found) {
return this;
}
List result = tail;
for (T next : preceding) {
result = result.prepend(next);
}
return result;
}
@Override
default List removeFirst(Predicate predicate) {
Objects.requireNonNull(predicate, "predicate is null");
List init = List.empty();
List 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 removeLast(Predicate predicate) {
Objects.requireNonNull(predicate, "predicate is null");
final List removedAndReversed = reverse().removeFirst(predicate);
return removedAndReversed.length() == length() ? this : removedAndReversed.reverse();
}
@Override
default List removeAt(int index) {
if (index < 0) {
throw new IndexOutOfBoundsException("removeAt(" + index + ")");
}
if (isEmpty()) {
throw new IndexOutOfBoundsException("removeAt(" + index + ") on Nil");
}
List init = Nil.instance();
List tail = this;
while (index > 0 && !tail.isEmpty()) {
init = init.prepend(tail.head());
tail = tail.tail();
index--;
}
if (index > 0 && tail.isEmpty()) {
throw new IndexOutOfBoundsException("removeAt() on Nil");
}
return init.reverse().appendAll(tail.tail());
}
@Override
default List removeAll(T removed) {
List result = Nil.instance();
boolean found = false;
for (T element : this) {
if (element.equals(removed)) {
found = true;
} else {
result = result.prepend(element);
}
}
return found ? result.reverse() : this;
}
@SuppressWarnings("unchecked")
@Override
default List removeAll(java.lang.Iterable extends T> elements) {
Objects.requireNonNull(elements, "elements is null");
// TODO(Eclipse bug): remove cast + SuppressWarnings
List removed = (List) (Object) List.ofAll(elements).distinct();
List result = Nil.instance();
boolean found = false;
for (T element : this) {
if (removed.contains(element)) {
found = true;
} else {
result = result.prepend(element);
}
}
return found ? result.reverse() : this;
}
@Override
default List replace(T currentElement, T newElement) {
List preceding = Nil.instance();
List 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 result = tail.tail().prepend(newElement);
for (T next : preceding) {
result = result.prepend(next);
}
return result;
}
@Override
default List replaceAll(T currentElement, T newElement) {
List result = Nil.instance();
for (List list = this; !list.isEmpty(); list = list.tail()) {
final T head = list.head();
final T elem = Objects.equals(head, currentElement) ? newElement : head;
result = result.prepend(elem);
}
return result.reverse();
}
@SuppressWarnings("unchecked")
@Override
default List retainAll(java.lang.Iterable extends T> elements) {
Objects.requireNonNull(elements, "elements is null");
// TODO(Eclipse bug): remove cast + SuppressWarnings
final List kept = (List) (Object) List.ofAll(elements).distinct();
List result = Nil.instance();
for (T element : this) {
if (kept.contains(element)) {
result = result.prepend(element);
}
}
return result.reverse();
}
@Override
default List reverse() {
return isEmpty() ? this : foldLeft(empty(), List::prepend);
}
@Override
default List scan(T zero, BiFunction super T, ? super T, ? extends T> operation) {
return scanLeft(zero, operation);
}
@Override
default List scanLeft(U zero, BiFunction super U, ? super T, ? extends U> operation) {
Objects.requireNonNull(operation, "operation is null");
return Collections.scanLeft(this, zero, operation, List.empty(), List::prepend, List::reverse);
}
@Override
default List scanRight(U zero, BiFunction super T, ? super U, ? extends U> operation) {
Objects.requireNonNull(operation, "operation is null");
return Collections.scanRight(this, zero, operation, List.empty(), List::prepend, Function.identity());
}
@Override
default List slice(int beginIndex, int endIndex) {
if (beginIndex >= endIndex || beginIndex >= length() || isEmpty()) {
return empty();
} else {
List result = Nil.instance();
List list = this;
final int lowerBound = Math.max(beginIndex, 0);
final int upperBound = Math.min(endIndex, length());
for (int i = 0; i < upperBound && !list.isEmpty(); i++) {
if (i >= lowerBound) {
result = result.prepend(list.head());
}
list = list.tail();
}
return result.reverse();
}
}
@Override
default Iterator> sliding(int size) {
return sliding(size, 1);
}
@Override
default Iterator> sliding(int size, int step) {
return iterator().sliding(size, step).map(List::ofAll);
}
@Override
default List sort() {
return isEmpty() ? this : toJavaStream().sorted().collect(List.collector());
}
@Override
default List sort(Comparator super T> comparator) {
Objects.requireNonNull(comparator, "comparator is null");
return isEmpty() ? this : toJavaStream().sorted(comparator).collect(List.collector());
}
@Override
default > List sortBy(Function super T, ? extends U> mapper) {
return sortBy(U::compareTo, mapper);
}
@Override
default List 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> span(Predicate super T> predicate) {
Objects.requireNonNull(predicate, "predicate is null");
final Tuple2, Iterator> itt = iterator().span(predicate);
return Tuple.of(List.ofAll(itt._1), List.ofAll(itt._2));
}
@Override
default Tuple2, List> splitAt(int n) {
if (isEmpty()) {
return Tuple.of(empty(), empty());
} else {
List init = Nil.instance();
List 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> splitAt(Predicate super T> predicate) {
if (isEmpty()) {
return Tuple.of(empty(), empty());
} else {
final Tuple2, List> 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> splitAtInclusive(Predicate super T> predicate) {
if (isEmpty()) {
return Tuple.of(empty(), empty());
} else {
final Tuple2, List> 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 Spliterator spliterator() {
return Spliterators.spliterator(iterator(), length(), Spliterator.ORDERED | Spliterator.IMMUTABLE);
}
@Override
default List subSequence(int beginIndex) {
if (beginIndex < 0) {
throw new IndexOutOfBoundsException("subSequence(" + beginIndex + ")");
}
List result = this;
for (int i = 0; i < beginIndex; i++, result = result.tail()) {
if (result.isEmpty()) {
throw new IndexOutOfBoundsException(
String.format("subSequence(%s) on List of length %s", beginIndex, i));
}
}
return result;
}
@Override
default List subSequence(int beginIndex, int endIndex) {
if (beginIndex < 0 || beginIndex > endIndex) {
throw new IndexOutOfBoundsException(
String.format("subSequence(%s, %s) on List of length %s", beginIndex, endIndex, length()));
}
List result = Nil.instance();
List list = this;
for (int i = 0; i < endIndex; i++, list = list.tail()) {
if (list.isEmpty()) {
throw new IndexOutOfBoundsException(
String.format("subSequence(%s, %s) on List of length %s", beginIndex, endIndex, i));
}
if (i >= beginIndex) {
result = result.prepend(list.head());
}
}
return result.reverse();
}
@Override
List tail();
@Override
default Option> tailOption() {
return isEmpty() ? None.instance() : new Some<>(tail());
}
@Override
default List take(int n) {
if (n >= length()) {
return this;
}
if (n <= 0) {
return empty();
}
List result = Nil.instance();
List list = this;
for (int i = 0; i < n && !list.isEmpty(); i++, list = list.tail()) {
result = result.prepend(list.head());
}
return result.reverse();
}
@Override
default List takeRight(int n) {
if (n >= length()) {
return this;
}
if (n <= 0) {
return empty();
}
return reverse().take(n).reverse();
}
@Override
default List takeUntil(Predicate super T> predicate) {
Objects.requireNonNull(predicate, "predicate is null");
return takeWhile(predicate.negate());
}
@Override
default List takeWhile(Predicate super T> predicate) {
Objects.requireNonNull(predicate, "predicate is null");
List result = Nil.instance();
for (List list = this; !list.isEmpty() && predicate.test(list.head()); list = list.tail()) {
result = result.prepend(list.head());
}
return result.length() == length() ? this : result.reverse();
}
@Override
default List unit(java.lang.Iterable extends U> iterable) {
return List.ofAll(iterable);
}
@Override
default Tuple2, List> unzip(
Function super T, Tuple2 extends T1, ? extends T2>> unzipper) {
Objects.requireNonNull(unzipper, "unzipper is null");
List xs = Nil.instance();
List 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 Tuple3, List, List> unzip3(
Function super T, Tuple3 extends T1, ? extends T2, ? extends T3>> unzipper) {
Objects.requireNonNull(unzipper, "unzipper is null");
List xs = Nil.instance();
List ys = Nil.instance();
List 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 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 preceding = Nil.instance();
List 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 result = tail.tail().prepend(element);
for (T next : preceding) {
result = result.prepend(next);
}
return result;
}
@Override
default List> zip(java.lang.Iterable that) {
Objects.requireNonNull(that, "that is null");
return List.ofAll(iterator().zip(that));
}
@Override
default List> zipAll(java.lang.Iterable that, T thisElem, U thatElem) {
Objects.requireNonNull(that, "that is null");
return List.ofAll(iterator().zipAll(that, thisElem, thatElem));
}
@Override
default List> zipWithIndex() {
return List.ofAll(iterator().zipWithIndex());
}
/**
* Representation of the singleton empty {@code List}.
*
* @param Component type of the List.
* @since 1.1.0
*/
final class Nil implements List, Serializable {
private static final long serialVersionUID = 1L;
private static final Nil> INSTANCE = new Nil<>();
// hidden
private Nil() {
}
/**
* Returns the singleton instance of the liked list.
*
* @param Component type of the List
* @return the singleton instance of the linked list.
*/
@SuppressWarnings("unchecked")
public static Nil instance() {
return (Nil) INSTANCE;
}
@Override
public T head() {
throw new NoSuchElementException("head of empty list");
}
@Override
public int length() {
return 0;
}
@Override
public List tail() {
throw new UnsupportedOperationException("tail of empty list");
}
@Override
public boolean isEmpty() {
return true;
}
@Override
public boolean equals(Object o) {
return o == this;
}
@Override
public int hashCode() {
return Traversable.hash(this);
}
@Override
public String toString() {
return "List()";
}
/**
* 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 Component type of the List.
* @since 1.1.0
*/
// DEV NOTE: class declared final because of serialization proxy pattern (see Effective Java, 2nd ed., p. 315)
final class Cons