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

javaslang.collection.List Maven / Gradle / Ivy

There is a newer version: 2.0.0-RC4
Show newest version
/*     / \____  _    _  ____   ______  / \ ____  __    _ _____
 *    /  /    \/ \  / \/    \ /  /\__\/  //    \/  \  / /  _  \   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 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 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 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 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 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 predicate) { Objects.requireNonNull(predicate, "predicate is null"); return dropWhile(predicate.negate()); } @Override default List dropWhile(Predicate 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 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> 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 action) { Objects.requireNonNull(action, "action is null"); Stack.super.forEach(action); } @Override default boolean forAll(Predicate 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 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 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 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 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 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 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 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 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 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 operation) { return scanLeft(zero, operation); } @Override default List scanLeft(U zero, BiFunction 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 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 comparator) { Objects.requireNonNull(comparator, "comparator is null"); return isEmpty() ? this : toJavaStream().sorted(comparator).collect(List.collector()); } @Override default > List sortBy(Function mapper) { return sortBy(U::compareTo, mapper); } @Override default List 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, List> span(Predicate 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 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 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 predicate) { Objects.requireNonNull(predicate, "predicate is null"); return takeWhile(predicate.negate()); } @Override default List takeWhile(Predicate 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 iterable) { return List.ofAll(iterable); } @Override default Tuple2, List> unzip( Function> unzipper) { Objects.requireNonNull(unzipper, "unzipper is null"); List xs = Nil.instance(); List ys = Nil.instance(); for (T element : this) { final Tuple2 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> 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 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 implements List, Serializable { private static final long serialVersionUID = 1L; private final T head; private final List tail; private final int length; private final transient Lazy hashCode = Lazy.of(() -> Traversable.hash(this)); /** * Creates a List consisting of a head value and a trailing List. * * @param head The head * @param tail The tail */ public Cons(T head, List 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 tail() { return tail; } @Override public boolean isEmpty() { return false; } @Override public boolean equals(Object o) { if (o == this) { return true; } else if (o instanceof List) { List list1 = this; List list2 = (List) o; while (!list1.isEmpty() && !list2.isEmpty()) { final boolean isEqual = Objects.equals(list1.head(), list2.head()); if (!isEqual) { return false; } list1 = list1.tail(); list2 = list2.tail(); } return list1.isEmpty() && list2.isEmpty(); } else { return false; } } @Override public int hashCode() { return hashCode.get(); } @Override public String toString() { return mkString("List(", ", ", ")"); } /** * {@code writeReplace} method for the serialization proxy pattern. *

* The presence of this method causes the serialization system to emit a SerializationProxy instance instead of * an instance of the enclosing class. * * @return A SerialiationProxy for this enclosing class. */ private Object writeReplace() { return new SerializationProxy<>(this); } /** * {@code readObject} method for the serialization proxy pattern. *

* 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". */ 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 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. private static final class SerializationProxy implements Serializable { private static final long serialVersionUID = 1L; // the instance to be serialized/deserialized private transient Cons list; /** * Constructor for the case of serialization, called by {@link Cons#writeReplace()}. *

* 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 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 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 temp = Nil.instance(); for (int i = 0; i < size; i++) { @SuppressWarnings("unchecked") final T element = (T) s.readObject(); temp = temp.prepend(element); } list = (Cons) temp.reverse(); } /** * {@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 list; } } } } interface ListModule { final class Combinations { static List> apply(List 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)) ); } } } final class SplitAt { static Tuple2, List> splitByPredicateReversed(List source, Predicate predicate) { Objects.requireNonNull(predicate, "predicate is null"); List init = Nil.instance(); List tail = source; while (!tail.isEmpty() && !predicate.test(tail.head())) { init = init.prepend(tail.head()); tail = tail.tail(); } return Tuple.of(init, tail); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy