dev.marksman.collectionviews.Vector Maven / Gradle / Ivy
Show all versions of collection-views Show documentation
package dev.marksman.collectionviews;
import com.jnape.palatable.lambda.adt.Maybe;
import com.jnape.palatable.lambda.adt.hlist.Tuple2;
import com.jnape.palatable.lambda.functions.Fn1;
import com.jnape.palatable.lambda.functions.Fn2;
import dev.marksman.enhancediterables.FiniteIterable;
import dev.marksman.enhancediterables.ImmutableNonEmptyFiniteIterable;
import java.util.Iterator;
import java.util.List;
import java.util.RandomAccess;
import static com.jnape.palatable.lambda.adt.Maybe.*;
import static dev.marksman.collectionviews.EmptyVectorBuilder.emptyVectorBuilder;
/**
* A finite, ordered view of a collection.
*
* A {@code Vector} guarantees the following:
*
* - A {@link Vector#size} method that executes in O(1)*.
*
- A {@link Vector#get} method that retrieves an element by index in O(1)*.
*
- An {@link Vector#isEmpty} method that executes in O(1)*.
*
- Iteration will always terminate.
*
- Protected from mutation by the bearer.
*
- The bearer cannot gain access to a reference to the underlying collection.
*
*
* Additionally, the ability to transform the {@code Vector} to new {@code Vector}s, without
* affecting or copying the underlying collection, is provided through the following methods:
*
* - {@link Vector#fmap}
*
- {@link Vector#take}
*
- {@link Vector#drop}
*
- {@link Vector#slice}
*
*
* While {@code Vector} does implement {@link Iterable}, it does not implement {@link java.util.Collection}.
* If you need to convert the {@code Vector} to a {@code java.util.Collection}, you will need to make a copy.
* Consider using Lambda's {@code toCollection} function to accomplish this.
*
* *The claim of O(1) means that the number of elements in the Vector has no bearing on performance.
* However, the number of transformations applied to the Vector, such as mapping and slicing, will.
* Technically, the complexity is O(k) where k is the number of transformations applied.
*
* @param the element type
*/
public interface Vector extends FiniteIterable, RandomAccess {
/**
* Returns the size of this {@code Vector}.
*
* Executes in O(1).
*
* @return the number of elements in this {@code Vector}
*/
int size();
/**
* Gets an element from this {@code Vector} at an index.
*
* Executes in O(1).
*
* @param index the index of the element to retrieve.
* Must be between 0 and size() - 1
, otherwise will
* throw an {@link IndexOutOfBoundsException}
* @return the element at {@code index}. May be null, if the underlying data contains a null at that index.
* @throws IndexOutOfBoundsException if index is less than 0 or greater than or equal to {@code size()}
*/
A unsafeGet(int index);
/**
* Returns the cartesian product of this {@code Vector} with another {@code Vector}.
*
* Does not make copies of any underlying collections.
*
* The returned {@link Vector} will have a size of {@code size()} × {@code other.size()},
* but will allocate no extra memory (aside from a few bytes for housekeeping).
*
* @param other a {@code Vector} of any type
* @param the type of the other {@code Vector}
* @return a {@code Vector>}
*/
default Vector> cross(Vector other) {
return Vectors.cross(this, other);
}
/**
* Returns a new {@code Vector} that drops the first {@code count} elements of this {@code Vector}.
*
* Does not make copies of any underlying collections.
*
* Use caution when taking a small slice of a huge {@code Vector} that you no longer need.
* The smaller slice will hold onto a reference of the larger one, and will prevent it from being GC'ed.
*
* @param count the number of elements to drop from this {@code Vector}.
* Must be >= 0.
* May exceed size of this {@code Vector}, in which case, the result will be an
* empty {@code Vector}.
* @return a {@code Vector}
*/
@Override
default Vector drop(int count) {
return Vectors.drop(count, this);
}
/**
* Returns a new {@code Vector} that drops all except the last {@code count} elements of this {@code Vector}.
*
* Does not make copies of any underlying collections.
*
* Use caution when taking a small slice of a huge {@code Vector} that you no longer need.
* The smaller slice will hold onto a reference of the larger one, and will prevent it from being GC'ed.
*
* @param count the number of elements to drop from the end of this {@code Vector}.
* Must be >= 0.
* May exceed size of this {@code Vector}, in which case, the result will be an
* empty {@code Vector}.
* @return a {@code Vector}
*/
default Vector dropRight(int count) {
return Vectors.dropRight(count, this);
}
/**
* Finds the first element of this {@code Vector} that satisfies a predicate, if any, and returns its index.
*
* @param predicate a predicate; not null
* @return an index wrapped in a {@link Maybe#just} if a matching element is found;
* {@link Maybe#nothing} otherwise.
*/
default Maybe findIndex(Fn1 super A, ? extends Boolean> predicate) {
return Vectors.findIndex(predicate, this);
}
/**
* Maps a function over this {@code Vector}.
*
* Returns a new {@link Vector} of the same size (but possibly a different type).
*
* Does not make any copies of underlying collections.
*
* This method is stack-safe, so a {@code Vector} can be mapped as many times as the heap permits.
*
* @param f a function from {@code A} to {@code B}.
* Not null.
* This function should be referentially transparent and not perform side-effects.
* It may be called zero or more times for each element.
* @param The type of the elements contained in the output Vector.
* @return a {@code Vector} of the same size
*/
@Override
default Vector fmap(Fn1 super A, ? extends B> f) {
return Vectors.map(f, this);
}
/**
* Gets an element from this {@code Vector} at an index.
*
* Executes in O(1).
* Will never return null.
*
* @param index the index of the element to retrieve
* @return an element wrapped in a {@link Maybe#just} if the index is in range and the element is not null.
* {@link Maybe#nothing} otherwise.
*/
default Maybe get(int index) {
if (index >= 0 && index < size()) {
return maybe(unsafeGet(index));
} else {
return nothing();
}
}
/**
* Returns an {@code ImmutableVector} that contains all the indices of this {@code Vector}.
*
* @return an {@code ImmutableVector}
*/
default ImmutableVector indices() {
return ImmutableVectors.indices(this);
}
/**
* Returns a {@code NonEmptyIterable} containing the inits of this {@code Vector}.
*
* The first value will be this {@code Vector} and the final one will be an empty {@code Vector},
* with the intervening values the results of successive applications of {@code init}.
*
* @return a {@code NonEmptyIterable} over all the inits of this {@code Vector}
*/
@Override
default ImmutableNonEmptyFiniteIterable extends Vector> inits() {
return Vectors.inits(this);
}
/**
* Tests whether this {@code Vector} is empty.
*
* Executes in O(1).
*
* @return true if this {@code Vector} is empty, false otherwise.
*/
@Override
default boolean isEmpty() {
return size() == 0;
}
/**
* Returns an iterator over this {@code Vector}'s elements.
*
* @return an Iterator
*/
@Override
default Iterator iterator() {
return VectorHelpers.vectorIterator(this);
}
/**
* Creates a {@code Vector} with this {@code Vector}'s elements in reversed order.
*
* Does not make copies of any underlying collections.
*
* @return a {@code Vector}
*/
@Override
default Vector reverse() {
return Vectors.reverse(this);
}
/**
* Creates a slice of this {@code Vector}.
*
* Does not make copies of any underlying collections.
*
* Use caution when taking a small slice of a huge {@code Vector} that you no longer need.
* The smaller slice will hold onto a reference of the larger one, and will prevent it from being GC'ed.
* To avoid this situation, use {@link Vector#copySliceFrom} instead.
*
* @param startIndex the index of the element to begin the slice.
* Must be >= 0.
* May exceed the size of this {@code Vector}, in which case an empty {@code Vector} will be returned.
* @param endIndexExclusive the end index (exclusive) of the slice. Must be >= {@code startIndex}.
* May exceed the size of this {@code Vector}, in which case the slice will
* contain as many elements as available.
* @return a {@code Vector}
*/
default Vector slice(int startIndex, int endIndexExclusive) {
return Vectors.slice(startIndex, endIndexExclusive, this);
}
/**
* Splits this {@code Vector} into two at a given position.
*
* Does not make copies of any underlying collections.
*
* Note that vector.splitAt(n)
is equivalent to, but possibly more efficient than
* tuple(vector.take(n), vector.drop(n))
*
* @param index the position at which to split.
* Must be >= 0;
* @return a {@code Tuple2} contains of {@code Vector}s, one of which containing the first {@code index} elements,
* the second containing the other elements.
*/
default Tuple2 extends Vector, ? extends Vector> splitAt(int index) {
return Vectors.splitAt(index, this);
}
/**
* Returns a {@code NonEmptyIterable} containing the tails of this {@code Vector}.
*
* The first value will be this {@code Vector} and the final one will be an empty {@code Vector},
* with the intervening values the results of successive applications of {@code tail}.
*
* @return a {@code NonEmptyIterable} over all the tails of this {@code Vector}
*/
@Override
default ImmutableNonEmptyFiniteIterable extends Vector> tails() {
return Vectors.tails(this);
}
/**
* Returns a new {@code Vector} containing at most the first {@code count} elements of this {@code Vector}.
*
* Does not make copies of any underlying collections.
*
* Use caution when taking a small slice of a huge {@link Vector} that you no longer need.
* The smaller slice will hold onto a reference of the larger one, and will prevent it from being GC'ed.
* To avoid this situation, use {@link Vector#copyFrom(int, Iterable)} instead.
*
* @param count the maximum number of elements to take from this {@code Vector}.
* Must be >= 0.
* May exceed size of this {@code Vector}.
* @return a {@code Vector}
*/
@Override
default Vector take(int count) {
return Vectors.take(count, this);
}
/**
* Returns a new {@code Vector} containing at most the last {@code count} elements of this {@code Vector}.
*
* Does not make copies of any underlying collections.
*
* Use caution when taking a small slice of a huge {@link Vector} that you no longer need.
* The smaller slice will hold onto a reference of the larger one, and will prevent it from being GC'ed.
* To avoid this situation, use {@link Vector#copyFrom(int, Iterable)} instead.
*
* @param count the maximum number of elements to take from this {@code Vector}.
* Must be >= 0.
* May exceed size of this {@code Vector}.
* @return a {@code Vector}
*/
default Vector takeRight(int count) {
return Vectors.takeRight(count, this);
}
/**
* Converts this {@code Vector} to an {@code ImmutableVector}.
*
* This method will make a copy of the underlying data structure if necessary to guarantee immutability.
*
* If this {@link Vector} is already an {@link ImmutableVector}, no copies are made and this method is a no-op.
*
* @return an {@code ImmutableVector} of the same type and containing the same elements
*/
default ImmutableVector toImmutable() {
return ImmutableVectors.ensureImmutable(this);
}
/**
* Attempts to convert this {@code Vector} to a {@code NonEmptyVector}.
*
* If successful, returns a {@link NonEmptyVector} containing the same elements as this one, wrapped in a {@link Maybe#just}.
*
* If this {@code Vector} is empty, returns {@link Maybe#nothing}.
*
* Does not make copies of any underlying collections.
*
* @return a {@code Maybe>}
*/
@Override
default Maybe extends NonEmptyVector> toNonEmpty() {
return Vectors.maybeNonEmptyWrap(this);
}
/**
* Attempts to convert this {@code Vector} to a {@code NonEmptyVector}.
*
* If successful, returns a {@link NonEmptyVector} containing the same elements as this one.
* Use this if you are confident that this {@link Vector} is not empty.
*
* If this {@code Vector} is empty, throws an {@link IllegalArgumentException}.
*
* Does not make copies of any underlying collections.
*
* @return a {@code NonEmptyVector}
* @throws IllegalArgumentException if this {@code Vector} is empty
*/
default NonEmptyVector toNonEmptyOrThrow() {
return Vectors.nonEmptyWrapOrThrow(this);
}
/**
* Zips together this {@code Vector} with another {@code Vector} by applying a zipping function.
*
* Applies the function to the successive elements of of each {@code Vector} until one of them runs out of elements.
*
* Does not make copies of any underlying collections.
*
* @param fn The zipping function.
* Not null.
* This function should be referentially transparent and not perform side-effects.
* It may be called zero or more times for each element.
* @param other The other {@code Vector}
* @param The element type of the other {@code Vector}
* @param The element type of the result
* @return A {@code Vector}
*/
default Vector zipWith(Fn2 fn, Vector other) {
return Vectors.zipWith(fn, this, other);
}
/**
* Zips this {@code Vector} with its indices.
*
* Does not make copies of any underlying collections.
*
* @return a new {@code Vector} containing pairs consisting of all elements of this {@code Vector} paired with their index.
* Indices start at 0.
*/
default Vector> zipWithIndex() {
return Vectors.zipWithIndex(this);
}
/**
* Returns an empty {@code ImmutableVector}.
*
* @param the element type
* @return an empty {@code ImmutableVector}
*/
static ImmutableVector empty() {
return Vectors.empty();
}
/**
* Creates a {@code ImmutableNonEmptyVector} with the given elements.
*
* @param first the first element
* @param more the remaining elements
* @param the element type
* @return an {@code ImmutableNonEmptyVector}
*/
@SuppressWarnings("varargs")
@SafeVarargs
static ImmutableNonEmptyVector of(A first, A... more) {
return Vectors.nonEmptyVectorOf(first, more);
}
/**
* Creates a new {@code VectorBuilder}.
*
* @param the element type
* @return an empty {@link VectorBuilder}
*/
static VectorBuilder builder() {
return emptyVectorBuilder(nothing());
}
/**
* Creates a new {@code VectorBuilder} with an initial capacity hint.
*
* @param initialCapacity an initial capacity hint.
* Must be >= 0.
* @param the element type
* @return an empty {@link VectorBuilder}
*/
static VectorBuilder builder(int initialCapacity) {
return emptyVectorBuilder(just(initialCapacity));
}
/**
* Creates an {@code ImmutableVector} that repeats the same element {@code size} times.
*
* Uses O(1) memory.
*
* See {@link NonEmptyVector#fill} if you require an {@link ImmutableNonEmptyVector} to be returned.
*
* @param size the number of elements.
* Must be >= 0.
* @param value the value that will be repeated for all elements of the {@code ImmutableVector}
* @param the element type
* @return an {@code ImmutableVector} of {@code size} elements, with each element having
* the value {@code value}
*/
static ImmutableVector fill(int size, A value) {
return ImmutableVectors.fill(size, value);
}
/**
* Creates an {@code ImmutableVector} where elements are lazily evaluated.
*
* Uses O(1) memory.
*
* See {@link NonEmptyVector#lazyFill} if you require a {@link ImmutableNonEmptyVector} to be returned.
*
* @param size the number of elements.
* Must be >= 0.
* @param valueSupplier a function that accepts an index and returns the computed value for that index.
* Not null.
* This function should be referentially transparent and not perform side-effects.
* It may be called zero or more times for each element.
* @param the element type
* @return an {@code ImmutableVector}
*/
static ImmutableVector lazyFill(int size, Fn1 valueSupplier) {
return ImmutableVectors.lazyFill(size, valueSupplier);
}
/**
* Creates an {@code ImmutableVector} containing elements 0..size - 1
.
* In other words, each element of the returned {@code Vector} will contains its index.
*
* Uses O(1) memory.
*
* @param size the number of elements.
* Must be >= 0.
* If 0, the returned {@code ImmutableVector} will be empty.
* @return an {@code ImmutableVector}
*/
static ImmutableVector range(int size) {
return ImmutableVectors.range(size);
}
/**
* Creates a {@code Vector} that wraps an array.
*
* Does not make any copies of the given array.
* The created {@link Vector} will hold on to a reference to the array, but will never alter it in any way.
*
* Bearers of the created {@code Vector} will be unable to gain access to the underlying array, so it is safe to share.
*
* Since no copy is made, be aware that anyone that holds a direct reference to the array can still mutate it.
* Use {@link Vector#copyFrom} instead if you want to avoid this situation.
*
* @param underlying array to wrap; not null
* @param the element type
* @return a {@code Vector}
*/
static Vector wrap(A[] underlying) {
return Vectors.wrap(underlying);
}
/**
* Creates a {@code Vector} that wraps a {@code java.util.List}.
*
* Does not make any copies of the given {@link java.util.List}.
* The created {@link Vector} will hold a reference to the given {@code List}, but will not alter it in any way.
*
* Bearers of the created {@code Vector} will be unable to gain access to the underlying {@code List}, so it is safe to share.
*
* Since no copy is made, be aware that anyone that holds a direct reference to the {@code List} can still mutate it.
* Mutating the {@code List} is not advised.
* Operations that change the size of the underlying {@code List} will result in unpredictable behavior.
* Use {@link Vector#copyFrom} if you want to avoid this situation.
*
* @param underlying {@code List} to wrap; not null
* @param the element type
* @return a {@code Vector}
*/
static Vector wrap(List underlying) {
return Vectors.wrap(underlying);
}
/**
* Creates an {@code ImmutableVector} that is copied from any {@code Iterable}.
*
* The entire {@link Iterable} will be eagerly iterated.
* Be careful not to pass in an infinite {@code Iterable} or this method will not terminate.
*
* If necessary to guarantee immutability, this method will make a copy of the data provided.
* If {@code source} is an untransformed {@link ImmutableVector}, it will be returned directly.
*
* @param source an {@code Iterable} that will be iterated eagerly in its entirety; not null
* @param the element type
* @return an {@code ImmutableVector}
*/
static ImmutableVector copyFrom(Iterable source) {
return ImmutableVectors.copyFrom(source);
}
/**
* Creates an {@code ImmutableVector} that is copied from an array.
*
* @param source the array to copy from.
* Not null.
* This method will not alter or hold on to a reference of this array.
* @param the element type
* @return an {@code ImmutableVector}
*/
static ImmutableVector copyFrom(A[] source) {
return ImmutableVectors.copyFrom(source);
}
/**
* Creates an {@code ImmutableVector} that is copied from any {@code Iterable}, but consuming a maximum number of elements.
*
* The {@link Iterable} will be eagerly iterated, but only up to a maximum of {@code maxCount} elements.
* If {@code maxCount} elements are not available, then the all of the elements available will be returned.
*
* This method will make a copy of the data provided, unless {@code source} is
* an untransformed {@link ImmutableVector} and its size is less than or equal to {@code maxCount},
* in which case it will be returned directly.
*
* If {@code source} is an {@code ImmutableVector} that is greater than {@code maxCount} in size,
* a copy will always be made, therefore making it memory-safe to take a small slice of
* a huge {@link Vector} that you no longer need.
*
* @param maxCount the maximum number of elements to consume from the source.
* Must be >= 0.
* @param source an {@code Iterable} that will be iterated eagerly for up to {@code maxCount} elements.
* Not null.
* It is safe for {@code source} to be infinite.
* @param the element type
* @return an {@code ImmutableVector} that contains at most {@code maxCount} elements
*/
static ImmutableVector copyFrom(int maxCount, Iterable source) {
return ImmutableVectors.copyFrom(maxCount, source);
}
/**
* Returns a new {@code ImmutableVector} that is copied from an array.
*
* @param maxCount the maximum number of elements to copy from the array.
* Must be >= 0.
* @param source the array to copy from.
* Not null.
* This method will not alter or hold on to a reference of this array.
* @param the element type
* @return an {@code ImmutableVector}
*/
static ImmutableVector copyFrom(int maxCount, A[] source) {
return ImmutableVectors.copyFrom(maxCount, source);
}
/**
* Creates an {@code ImmutableVector} by copying a slice from an {@code Iterable}.
*
* The {@link Iterable} will be eagerly iterated, but only for the number of elements needed to fulfill the requested slice.
* If not enough elements are not available, then this method yields as many elements that were available.
*
* This method will make a copy of the data provided, except in the case {@code startIndex} is 0
* and {@code source} is an {@link ImmutableVector} whose size is less than or equal to {@code count},
* in which case it will be returned directly.
*
* It is memory-safe to use this method to take a small slice of a huge {@link Vector} that you no longer need.
*
* @param startIndex the index of the element to begin the slice.
* Must be >= 0.
* May exceed the size of the {@link Iterable}.
* @param endIndexExclusive the end index (exclusive) of the slice.
* Must be >= {@code startIndex}.
* May exceed the size of the {@link Iterable}.
* @param source an {@code Iterable} that will be iterated eagerly for up to {@code endIndexExclusive} elements.
* Not null.
* It is safe for {@code source} to be infinite.
* @param the element type
* @return an {@code ImmutableVector}
*/
static ImmutableVector copySliceFrom(int startIndex, int endIndexExclusive, Iterable source) {
return ImmutableVectors.copySliceFrom(startIndex, endIndexExclusive, source);
}
}