org.abego.commons.seq.Seq Maven / Gradle / Ivy
/*
* MIT License
*
* Copyright (c) 2020 Udo Borkowski, ([email protected])
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.abego.commons.seq;
import org.eclipse.jdt.annotation.Nullable;
import java.util.Comparator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;
/**
* A sequence of items of type T.
*
* A Seq is a way to abstract from implementation details of "classic"
* data types representing sequences, such as {@link List}s or arrays.
*
* In contrast to an {@link Iterable} or a {@link Stream} a Seq also provides
* the number of items in the sequence ({@link #size()}) as well as indexed
* access to the items ({@link #item(int)}). Also Seq introduces the concept of
* a "single item" sequence.
*/
public interface Seq extends Iterable {
String MORE_THAN_ONE_ELEMENT_MESSAGE = "More than one element"; //NON-NLS
String NO_SUCH_ELEMENT_MESSAGE = "No such element"; //NON-NLS
/**
* Return the size of the sequence.
*/
int size();
/**
* Return the index
-ed item in the sequence.
*
* index
is zero-based.
*/
T item(int index);
/**
* Return the index of the first occurrence of the specified item in this
* Seq, or -1 if this Seq does not contain the item.
*
* {@link Objects#equals(Object, Object)} is used to check for the occurrence.
*/
default int indexOf(@Nullable T item) {
int i = 0;
for (T o : this) {
if ((Objects.equals(item, o)))
return i;
i++;
}
return -1;
}
/**
* Return {@code true} when the Seq contains the specified item,
* {@code false} otherwise.
*
* {@link Objects#equals(Object, Object)} is used to check for the occurrence.
*/
default boolean contains(@Nullable T item) {
return indexOf(item) >= 0;
}
/**
* Return the items of the sequence as a {@link Stream}.
*/
default Stream stream() {
Stream.Builder builder = Stream.builder();
for (T o : this) {
builder.add(o);
}
return builder.build();
}
/**
* Return true
when the sequence is empty,
* false
otherwise.
*/
default boolean isEmpty() {
return size() == 0;
}
/**
* Return true
when the sequence has one or more items,
* false
otherwise.
*
* It is equivalent to {@code !isEmpty()}.
*
*/
default boolean hasItems() {
return !isEmpty();
}
/**
* Return true
when the sequence has exactly one item,
* false
otherwise.
*/
default boolean hasSingleItem() {
return size() == 1;
}
/**
* Return the first item of the sequence.
*
* Throw an {@link NoSuchElementException} when the sequence is empty.
*/
default T first() {
if (isEmpty()) {
throw new NoSuchElementException();
}
return item(0);
}
/**
* Returns a new Seq consisting of the items of this sequence that match
* the predicate
.
*
* See {@link SeqUtil#filter(Seq, Predicate)} for a default implementation.
*
* This method has no default implementation to avoid cyclic dependencies.
*/
Seq filter(Predicate condition);
/**
* Returns a new Seq consisting of the results of applying the given
* mapper
function to the elements of this Seq.
*
* See {@link SeqUtil#map(Seq, Function)} for a default implementation.
*
* This method has no default implementation to avoid cyclic dependencies.
*/
Seq map(Function super T, ? extends R> mapper);
/**
* Returns a new Seq consisting of the elements of this Seq
* sorted by the given sortKey
.
*/
> Seq sortedBy(Function sortKey);
/**
* Return a new Seq consisting of the elements of this Seq
* sorted in ascending order, according to the {@linkplain Comparable
* natural ordering} of its elements.
*
* All elements in the Seq must implement the {@link Comparable}
* interface. Furthermore, all elements in the Seq must be
* mutually comparable (that is, {@code e1.compareTo(e2)}
* must not throw a {@code ClassCastException} for any elements
* {@code e1} and {@code e2} in the Seq).
*/
Seq sorted();
/**
* Return a new Seq consisting of the elements of this Seq
* sorted in ascending order, according to the given comparator
.
*
* All elements in this list must be mutually comparable using the
* specified comparator (that is, {@code c.compare(e1, e2)} must not throw
* a {@code ClassCastException} for any elements {@code e1} and {@code e2}
* in the list).
*/
Seq sorted(Comparator super T> comparator);
/**
* Return the one and only item of this sequence.
*
* Throw a {@link NoSuchElementException} when the sequence is empty or
* contains more than one item.
*/
default T singleItem() {
if (!hasSingleItem()) {
throw new NoSuchElementException(size() > 1 ? MORE_THAN_ONE_ELEMENT_MESSAGE : NO_SUCH_ELEMENT_MESSAGE);
}
return first();
}
/**
* Return the one and only item of this sequence, or null
when
* the sequence is empty.
*
* Throw a {@link NoSuchElementException} when the sequence contains more
* than one item.
*/
@Nullable
default T singleItemOrNull() {
if (!hasSingleItem()) {
if (isEmpty()) {
return null;
}
throw new NoSuchElementException(MORE_THAN_ONE_ELEMENT_MESSAGE);
}
return first();
}
/**
* Return any item of this sequence, or null
when
* the sequence is empty.
*/
@Nullable
default T anyItemOrNull() {
return isEmpty() ? null : anyItem();
}
/**
* Return any item of this sequence.
*
* Throw a {@link NoSuchElementException} when the sequence is empty.
*
* It is not required to always return the first item.
*/
default T anyItem() {
if (isEmpty()) {
throw new NoSuchElementException();
}
return first();
}
default String joined(CharSequence separator, Function mapToString) {
return String.join(separator, map(mapToString));
}
default String joined(CharSequence separator) {
return joined(separator, Object::toString);
}
default String joined() {
return joined("");
}
/**
* Return true
if the specified object
is a
* Seq and equal to this Seq, return false
otherwise.
*
* Two Seqs are equal if they contain the same elements in the same
* order. Two elements e1
and e2
are the same
* elements if (e1==null ? e2==null : e1.equals(e2))
.
*
*
This definition ensures that the equals method works properly
* across different implementations of the Seq interface.
*/
boolean equals(@Nullable Object object);
/**
* Return the hash code value for this Seq.
*
*
The hash code of a Seq is defined to be the result of the
* following calculation:
*
* int hashCode = 1;
* for (Object e : this) {
* hashCode = 31 * hashCode + (e == null ? 0 : e.hashCode());
* }
*
*
* This ensures that seq1.equals(seq2) implies that
* seq1.hashCode()==seq2.hashCode() for any two Seqs, seq1 and seq2,
* as required by the general contract of Object.hashCode().
*/
int hashCode();
}