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

io.vavr.collection.LinearSeq Maven / Gradle / Ivy

The newest version!
/* ____  ______________  ________________________  __________
 * \   \/   /      \   \/   /   __/   /      \   \/   /      \
 *  \______/___/\___\______/___/_____/___/\___\______/___/\___\
 *
 * 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} 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 linearSeq) { return (LinearSeq) linearSeq; } // -- Adjusted return types of Seq methods @Override LinearSeq append(T element); @Override LinearSeq appendAll(Iterable elements); @Override LinearSeq asJava(Consumer> action); @Override LinearSeq asJavaMutable(Consumer> 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 partialFunction); @Override LinearSeq> combinations(); @Override LinearSeq> combinations(int k); @Override Iterator> crossProduct(int power); @Override LinearSeq distinct(); @Override LinearSeq distinctBy(Comparator comparator); @Override LinearSeq distinctBy(Function keyExtractor); @Override LinearSeq drop(int n); @Override LinearSeq dropUntil(Predicate predicate); @Override LinearSeq dropWhile(Predicate predicate); @Override LinearSeq dropRight(int n); @Override LinearSeq dropRightUntil(Predicate predicate); @Override LinearSeq dropRightWhile(Predicate predicate); @Override LinearSeq filter(Predicate predicate); @Override LinearSeq filterNot(Predicate predicate); @Override LinearSeq flatMap(Function> mapper); @Override Map> groupBy(Function classifier); @Override Iterator> grouped(int size); @Override default int indexOfSlice(Iterable that, int from) { Objects.requireNonNull(that, "that is null"); return LinearSeqModule.Slice.indexOfSlice(this, that, from); } @Override default int indexWhere(Predicate 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> initOption(); @Override LinearSeq insert(int index, T element); @Override LinearSeq insertAll(int index, Iterable elements); @Override LinearSeq intersperse(T element); @Override default int lastIndexOfSlice(Iterable that, int end) { Objects.requireNonNull(that, "that is null"); return LinearSeqModule.Slice.lastIndexOfSlice(this, that, end); } @Override default int lastIndexWhere(Predicate 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 mapper); @Override LinearSeq orElse(Iterable other); @Override LinearSeq orElse(Supplier> supplier); @Override LinearSeq padTo(int length, T element); @Override LinearSeq patch(int from, Iterable that, int replaced); @Override Tuple2, ? extends LinearSeq> partition(Predicate predicate); @Override LinearSeq peek(Consumer action); @Override LinearSeq> permutations(); @Override LinearSeq prepend(T element); @Override LinearSeq prependAll(Iterable 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 elements); @Override LinearSeq replace(T currentElement, T newElement); @Override LinearSeq replaceAll(T currentElement, T newElement); @Override LinearSeq retainAll(Iterable 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 operation); @Override LinearSeq scanLeft(U zero, BiFunction operation); @Override LinearSeq scanRight(U zero, BiFunction operation); @Override default int segmentLength(Predicate 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> slideBy(Function classifier); @Override Iterator> sliding(int size); @Override Iterator> sliding(int size, int step); @Override LinearSeq sorted(); @Override LinearSeq sorted(Comparator comparator); @Override > LinearSeq sortBy(Function mapper); @Override LinearSeq sortBy(Comparator comparator, Function mapper); @Override Tuple2, ? extends LinearSeq> span(Predicate predicate); @Override LinearSeq subSequence(int beginIndex); @Override LinearSeq subSequence(int beginIndex, int endIndex); @Override LinearSeq tail(); @Override Option> tailOption(); @Override LinearSeq take(int n); @Override LinearSeq takeUntil(Predicate predicate); @Override LinearSeq takeWhile(Predicate predicate); @Override LinearSeq takeRight(int n); @Override LinearSeq takeRightUntil(Predicate predicate); @Override LinearSeq takeRightWhile(Predicate predicate); @Override LinearSeq update(int index, T element); @Override LinearSeq update(int index, Function updater); @Override LinearSeq> zip(Iterable that); @Override LinearSeq zipWith(Iterable that, BiFunction mapper); @Override LinearSeq> zipAll(Iterable that, T thisElem, U thatElem); @Override LinearSeq> zipWithIndex(); @Override LinearSeq zipWithIndex(BiFunction 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} */ @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 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 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 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 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); } } }