io.vavr.collection.LinearSeq Maven / Gradle / Ivy
Show all versions of vavr Show documentation
/* ____ ______________ ________________________ __________
* \ \/ / \ \/ / __/ / \ \/ / \
* \______/___/\___\______/___/_____/___/\___\______/___/\___\
*
* Copyright 2021 Vavr, https://vavr.io
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.vavr.collection;
import io.vavr.*;
import io.vavr.control.Option;
import java.util.Comparator;
import java.util.Objects;
import java.util.Random;
import java.util.function.*;
/**
* Interface for immutable, linear sequences.
*
* Efficient {@code head()}, {@code tail()}, and {@code isEmpty()} methods are characteristic for linear sequences.
*
* @param component type
*/
public interface LinearSeq extends Seq {
long serialVersionUID = 1L;
/**
* Narrows a widened {@code LinearSeq extends T>} to {@code LinearSeq}
* by performing a type-safe cast. This is eligible because immutable/read-only
* collections are covariant.
*
* @param linearSeq A {@code LinearSeq}.
* @param Component type of the {@code LinearSeq}.
* @return the given {@code linearSeq} instance as narrowed type {@code LinearSeq}.
*/
@SuppressWarnings("unchecked")
static LinearSeq narrow(LinearSeq extends T> linearSeq) {
return (LinearSeq) linearSeq;
}
// -- Adjusted return types of Seq methods
@Override
LinearSeq append(T element);
@Override
LinearSeq appendAll(Iterable extends T> elements);
@Override
LinearSeq asJava(Consumer super java.util.List> action);
@Override
LinearSeq asJavaMutable(Consumer super java.util.List> action);
@Override
default PartialFunction asPartialFunction() throws IndexOutOfBoundsException {
return new PartialFunction() {
private static final long serialVersionUID = 1L;
@Override
public T apply(Integer index) {
return get(index);
}
@Override
public boolean isDefinedAt(Integer index) {
// we can't use length() because of infinite long sequences
return 0 <= index && drop(index).nonEmpty();
}
};
}
@Override
LinearSeq collect(PartialFunction super T, ? extends R> partialFunction);
@Override
LinearSeq extends LinearSeq> combinations();
@Override
LinearSeq extends LinearSeq> combinations(int k);
@Override
Iterator extends LinearSeq> crossProduct(int power);
@Override
LinearSeq distinct();
@Override
LinearSeq distinctBy(Comparator super T> comparator);
@Override
LinearSeq distinctBy(Function super T, ? extends U> keyExtractor);
@Override
LinearSeq drop(int n);
@Override
LinearSeq dropUntil(Predicate super T> predicate);
@Override
LinearSeq dropWhile(Predicate super T> predicate);
@Override
LinearSeq dropRight(int n);
@Override
LinearSeq dropRightUntil(Predicate super T> predicate);
@Override
LinearSeq dropRightWhile(Predicate super T> predicate);
@Override
LinearSeq filter(Predicate super T> predicate);
@Override
LinearSeq filterNot(Predicate super T> predicate);
@Override
LinearSeq flatMap(Function super T, ? extends Iterable extends U>> mapper);
@Override
Map> groupBy(Function super T, ? extends C> classifier);
@Override
Iterator extends LinearSeq> grouped(int size);
@Override
default int indexOfSlice(Iterable extends T> that, int from) {
Objects.requireNonNull(that, "that is null");
return LinearSeqModule.Slice.indexOfSlice(this, that, from);
}
@Override
default int indexWhere(Predicate super T> predicate, int from) {
Objects.requireNonNull(predicate, "predicate is null");
int i = from;
LinearSeq these = drop(from);
while (!these.isEmpty()) {
if (predicate.test(these.head())) {
return i;
}
i++;
these = these.tail();
}
return -1;
}
@Override
LinearSeq init();
@Override
Option extends LinearSeq> initOption();
@Override
LinearSeq insert(int index, T element);
@Override
LinearSeq insertAll(int index, Iterable extends T> elements);
@Override
LinearSeq intersperse(T element);
@Override
default int lastIndexOfSlice(Iterable extends T> that, int end) {
Objects.requireNonNull(that, "that is null");
return LinearSeqModule.Slice.lastIndexOfSlice(this, that, end);
}
@Override
default int lastIndexWhere(Predicate super T> predicate, int end) {
Objects.requireNonNull(predicate, "predicate is null");
int i = 0;
LinearSeq these = this;
int last = -1;
while (!these.isEmpty() && i <= end) {
if (predicate.test(these.head())) {
last = i;
}
these = these.tail();
i++;
}
return last;
}
@Override
LinearSeq map(Function super T, ? extends U> mapper);
@Override
LinearSeq orElse(Iterable extends T> other);
@Override
LinearSeq orElse(Supplier extends Iterable extends T>> supplier);
@Override
LinearSeq padTo(int length, T element);
@Override
LinearSeq patch(int from, Iterable extends T> that, int replaced);
@Override
Tuple2 extends LinearSeq, ? extends LinearSeq> partition(Predicate super T> predicate);
@Override
LinearSeq peek(Consumer super T> action);
@Override
LinearSeq extends LinearSeq> permutations();
@Override
LinearSeq prepend(T element);
@Override
LinearSeq prependAll(Iterable extends T> elements);
@Override
LinearSeq remove(T element);
@Override
LinearSeq removeFirst(Predicate predicate);
@Override
LinearSeq removeLast(Predicate predicate);
@Override
LinearSeq removeAt(int index);
@Override
LinearSeq removeAll(T element);
@Override
LinearSeq removeAll(Iterable extends T> elements);
@Override
LinearSeq replace(T currentElement, T newElement);
@Override
LinearSeq replaceAll(T currentElement, T newElement);
@Override
LinearSeq retainAll(Iterable extends T> elements);
@Override
LinearSeq reverse();
@Override
default Iterator reverseIterator() {
return reverse().iterator();
}
@Override
LinearSeq rotateLeft(int n);
@Override
LinearSeq rotateRight(int n);
@Override
LinearSeq shuffle();
@Override
LinearSeq shuffle(Random random);
@Override
LinearSeq scan(T zero, BiFunction super T, ? super T, ? extends T> operation);
@Override
LinearSeq scanLeft(U zero, BiFunction super U, ? super T, ? extends U> operation);
@Override
LinearSeq scanRight(U zero, BiFunction super T, ? super U, ? extends U> operation);
@Override
default int segmentLength(Predicate super T> predicate, int from) {
Objects.requireNonNull(predicate, "predicate is null");
int i = 0;
LinearSeq these = this.drop(from);
while (!these.isEmpty() && predicate.test(these.head())) {
i++;
these = these.tail();
}
return i;
}
@Override
LinearSeq slice(int beginIndex, int endIndex);
@Override
Iterator extends LinearSeq> slideBy(Function super T, ?> classifier);
@Override
Iterator extends LinearSeq> sliding(int size);
@Override
Iterator extends LinearSeq> sliding(int size, int step);
@Override
LinearSeq sorted();
@Override
LinearSeq sorted(Comparator super T> comparator);
@Override
> LinearSeq sortBy(Function super T, ? extends U> mapper);
@Override
LinearSeq sortBy(Comparator super U> comparator, Function super T, ? extends U> mapper);
@Override
Tuple2 extends LinearSeq, ? extends LinearSeq> span(Predicate super T> predicate);
@Override
LinearSeq subSequence(int beginIndex);
@Override
LinearSeq subSequence(int beginIndex, int endIndex);
@Override
LinearSeq tail();
@Override
Option extends LinearSeq> tailOption();
@Override
LinearSeq take(int n);
@Override
LinearSeq takeUntil(Predicate super T> predicate);
@Override
LinearSeq takeWhile(Predicate super T> predicate);
@Override
LinearSeq takeRight(int n);
@Override
LinearSeq takeRightUntil(Predicate super T> predicate);
@Override
LinearSeq takeRightWhile(Predicate super T> predicate);
@Override
LinearSeq update(int index, T element);
@Override
LinearSeq update(int index, Function super T, ? extends T> updater);
@Override
LinearSeq> zip(Iterable extends U> that);
@Override
LinearSeq zipWith(Iterable extends U> that, BiFunction super T, ? super U, ? extends R> mapper);
@Override
LinearSeq> zipAll(Iterable extends U> that, T thisElem, U thatElem);
@Override
LinearSeq> zipWithIndex();
@Override
LinearSeq zipWithIndex(BiFunction super T, ? super Integer, ? extends U> mapper);
/**
* Searches this sequence for a specific element using a linear search. The sequence must already be sorted into
* ascending natural order. If it is not sorted, the results are undefined.
*
* @param element the element to find
* @return the index of the search element, if it is contained in the sequence;
* otherwise, (-(insertion point) - 1)
. The
* insertion point is defined as the point at which the
* element would be inserted into the sequence. Note that this guarantees that
* the return value will be >= 0 if and only if the element is found.
* @throws ClassCastException if T cannot be cast to {@code Comparable super T>}
*/
@Override
@SuppressWarnings("unchecked")
default int search(T element) {
final ToIntFunction comparison = ((Comparable) element)::compareTo;
return LinearSeqModule.Search.linearSearch(this, comparison);
}
/**
* Searches this sequence for a specific element using a linear search. The sequence must already be sorted into
* ascending order according to the specified comparator. If it is not sorted, the results are undefined.
*
* @param element the element to find
* @param comparator the comparator by which this sequence is ordered
* @return the index of the search element, if it is contained in the sequence;
* otherwise, (-(insertion point) - 1)
. The
* insertion point is defined as the point at which the
* element would be inserted into the sequence. Note that this guarantees that
* the return value will be >= 0 if and only if the element is found.
*/
@Override
default int search(T element, Comparator super T> comparator) {
Objects.requireNonNull(comparator, "comparator is null");
final ToIntFunction comparison = current -> comparator.compare(element, current);
return LinearSeqModule.Search.linearSearch(this, comparison);
}
}
interface LinearSeqModule {
class Slice {
static int indexOfSlice(LinearSeq source, Iterable extends T> slice, int from) {
if (source.isEmpty()) {
return from == 0 && Collections.isEmpty(slice) ? 0 : -1;
}
final LinearSeq _slice = toLinearSeq(slice);
return findFirstSlice(source, _slice, Math.max(from, 0));
}
static int lastIndexOfSlice(LinearSeq source, Iterable extends T> slice, int end) {
if (end < 0) {
return -1;
} else if (source.isEmpty()) {
return Collections.isEmpty(slice) ? 0 : -1;
} else if (Collections.isEmpty(slice)) {
final int len = source.length();
return Math.min(len, end);
}
int index = 0;
int result = -1;
final LinearSeq _slice = toLinearSeq(slice);
while (source.length() >= _slice.length()) {
final Tuple2, Integer> r = findNextSlice(source, _slice);
if (r == null) {
return result;
}
if (index + r._2 <= end) {
result = index + r._2;
index += r._2 + 1;
source = r._1.tail();
} else {
return result;
}
}
return result;
}
private static int findFirstSlice(LinearSeq source, LinearSeq slice, int from) {
int index = 0;
final int sliceLength = slice.length();
// DEV-NOTE: we can't compute the length of an infinite Stream but it may contain a slice
final Predicate> hasMore = source.isLazy() ? LinearSeq::nonEmpty : seq -> seq.length() >= sliceLength;
while (hasMore.test(source)) {
if (index >= from && source.startsWith(slice)) {
return index;
}
index++;
source = source.tail();
}
return -1;
}
private static Tuple2, Integer> findNextSlice(LinearSeq source, LinearSeq slice) {
int index = 0;
while (source.length() >= slice.length()) {
if (source.startsWith(slice)) {
return Tuple.of(source, index);
}
index++;
source = source.tail();
}
return null;
}
@SuppressWarnings("unchecked")
private static LinearSeq toLinearSeq(Iterable extends T> iterable) {
return (iterable instanceof LinearSeq) ? (LinearSeq) iterable : List.ofAll(iterable);
}
}
interface Search {
static int linearSearch(LinearSeq seq, ToIntFunction comparison) {
int idx = 0;
for (T current : seq) {
final int cmp = comparison.applyAsInt(current);
if (cmp == 0) {
return idx;
} else if (cmp < 0) {
return -(idx + 1);
}
idx += 1;
}
return -(idx + 1);
}
}
}