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

net.digitalid.utility.functional.iterables.FunctionalIterable Maven / Gradle / Ivy

The newest version!
package net.digitalid.utility.functional.iterables;

import java.util.Iterator;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import net.digitalid.utility.annotations.generics.Specifiable;
import net.digitalid.utility.annotations.generics.Unspecifiable;
import net.digitalid.utility.annotations.method.Pure;
import net.digitalid.utility.annotations.ownership.Capturable;
import net.digitalid.utility.annotations.ownership.NonCapturable;
import net.digitalid.utility.functional.exceptions.IterationException;
import net.digitalid.utility.functional.failable.FailablePredicate;
import net.digitalid.utility.functional.failable.FailableUnaryFunction;
import net.digitalid.utility.functional.iterators.PruningIterator;
import net.digitalid.utility.functional.iterators.ReadOnlyIterator;
import net.digitalid.utility.functional.iterators.ZippingIterator;
import net.digitalid.utility.tuples.Pair;
import net.digitalid.utility.validation.annotations.math.NonNegative;
import net.digitalid.utility.validation.annotations.math.Positive;
import net.digitalid.utility.validation.annotations.type.ReadOnly;

/**
 * This interface extends the {@link Iterable} interface with functional methods.
 * 
 * @see InfiniteIterable
 * @see FiniteIterable
 */
@ReadOnly
public interface FunctionalIterable<@Specifiable ELEMENT> extends Iterable {
    
    /* -------------------------------------------------- Iterable -------------------------------------------------- */
    
    @Pure
    @Override
    public @Capturable @Nonnull ReadOnlyIterator iterator();
    
    /* -------------------------------------------------- Size -------------------------------------------------- */
    
    /**
     * Returns whether this iterable is empty.
     */
    @Pure
    public default boolean isEmpty() {
        return !iterator().hasNext();
    }
    
    /**
     * Returns the size of this iterable or the given limit if the size is greater than the limit.
     * 
     * @throws IndexOutOfBoundsException if the given limit is non-positive.
     */
    @Pure
    public default @NonNegative int size(@Positive int limit) {
        if (limit <= 0) { throw new IndexOutOfBoundsException("The limit has to be positive but was " + limit + "."); }
        
        int size = 0;
        final @Nonnull Iterator iterator = iterator();
        while (iterator.hasNext() && size < limit) {
            iterator.next();
            size++;
        }
        return size;
    }
    
    /**
     * Returns whether this iterable has the given number of elements.
     */
    @Pure
    public default boolean hasSize(@NonNegative int number) {
        return size(number + 1) == number;
    }
    
    /**
     * Returns whether this iterable contains a single element.
     */
    @Pure
    public default boolean isSingle() {
        return hasSize(1);
    }
    
    /**
     * Returns whether this iterable is empty or single.
     */
    @Pure
    public default boolean isEmptyOrSingle() {
        return hasSize(1);
    }
    
    /**
     * Returns whether the size of this iterable is at most the given value.
     */
    @Pure
    public default boolean sizeAtMost(@NonNegative int value) {
        return size(value + 1) <= value;
    }
    
    /**
     * Returns whether the size of this iterable is at least the given value.
     */
    @Pure
    public default boolean sizeAtLeast(@Positive int value) {
        return size(value) == value;
    }
    
    /* -------------------------------------------------- Element -------------------------------------------------- */
    
    /**
     * Returns the element at the given index.
     * 
     * @throws IndexOutOfBoundsException if the given index is negative or greater or equal to the size of this iterable.
     */
    @Pure
    public default @NonCapturable ELEMENT get(int index) {
        int currentIndex = 0;
        for (ELEMENT element : this) {
            if (currentIndex == index) { return element; }
            currentIndex += 1;
        }
        throw new IndexOutOfBoundsException("The index has to be non-negative and smaller than the size but was " + index + ".");
    }
    
    /* -------------------------------------------------- Filtering -------------------------------------------------- */
    
    /**
     * Returns the elements of this iterable that satisfy the given predicate.
     * Iterating over the returned iterable can throw a {@link IterationException}.
     */
    @Pure
    public @Nonnull FunctionalIterable filter(@Nonnull FailablePredicate predicate);
    
    /**
     * Returns the elements of this iterable that do not satisfy the given predicate.
     * Iterating over the returned iterable can throw a {@link IterationException}.
     */
    @Pure
    public @Nonnull FunctionalIterable filterNot(@Nonnull FailablePredicate predicate);
    
    /**
     * Returns the elements of this iterable without the null values.
     */
    @Pure
    public @Nonnull FunctionalIterable filterNulls();
    
    /* -------------------------------------------------- Mapping -------------------------------------------------- */
    
    /**
     * Returns the elements of this iterable mapped by the given function.
     * Iterating over the returned iterable can throw a {@link IterationException}.
     */
    @Pure
    public <@Specifiable TYPE> @Nonnull FunctionalIterable map(@Nonnull FailableUnaryFunction function);
    
    /* -------------------------------------------------- Instance -------------------------------------------------- */
    
    /**
     * Returns the elements of this iterable which are an instance of the given type.
     */
    @Pure
    public <@Specifiable TYPE> @Nonnull FunctionalIterable instanceOf(@Nonnull Class type);
    
    /* -------------------------------------------------- Pruning -------------------------------------------------- */
    
    /**
     * Returns the elements of this iterable after discarding the given number of elements from the beginning.
     */
    @Pure
    public @Nonnull FunctionalIterable skip(@Positive int number);
    
    /**
     * Returns the given number of elements from the beginning of this iterable.
     */
    @Pure
    public default @Nonnull FiniteIterable limit(@Positive int number) {
        return () -> PruningIterator.with(iterator(), 0, number);
    }
    
    /**
     * Returns the elements of this iterable from the given start index to but not including the given end index.
     * If the end index is {@link Integer#MAX_VALUE}, the returned iterable iterates as long as this iterable does,
     * which means calling {@code extract(startIndex, Integer.MAX_VALUE)} is the same as {@code skip(startIndex)}.
     */
    @Pure
    public default @Nonnull FiniteIterable extract(@Positive int startIndex, @Positive int endIndex) {
        return () -> PruningIterator.with(iterator(), startIndex, endIndex);
    }
    
    /* -------------------------------------------------- Zipping -------------------------------------------------- */
    
    /**
     * Returns the elements from this and the given iterable as pairs, where the i-th pair contains the i-th element of each iterable.
     * The returned iterable is truncated to the length of the shorter iterable.
     */
    @Pure
    public default <@Specifiable TYPE> @Nonnull FiniteIterable<@Nonnull Pair> zipShortest(@Nonnull FiniteIterable iterable) {
        return () -> ZippingIterator.with(iterator(), iterable.iterator(), true);
    }
    
    /**
     * Returns the elements from this and the given iterable as pairs, where the i-th pair contains the i-th element of each iterable.
     * The returned iterable is truncated to the length of the shorter iterable.
     */
    @Pure
    public <@Specifiable TYPE> @Nonnull FunctionalIterable<@Nonnull Pair> zipShortest(@Nonnull InfiniteIterable iterable);
    
    /**
     * Returns the elements from this and the given iterable as pairs, where the i-th pair contains the i-th element of each iterable.
     * The shorter iterable is extended to the length of the longer iterable with null values for the missing elements.
     */
    @Pure
    public <@Unspecifiable TYPE> @Nonnull FunctionalIterable<@Nonnull Pair<@Nullable ELEMENT, @Nullable TYPE>> zipLongest(@Nonnull FiniteIterable iterable);
    
    /**
     * Returns the elements from this and the given iterable as pairs, where the i-th pair contains the i-th element of each iterable.
     * The shorter iterable is extended to the length of the longer iterable with null values for the missing elements.
     */
    @Pure
    public default <@Specifiable TYPE> @Nonnull InfiniteIterable<@Nonnull Pair<@Nullable ELEMENT, TYPE>> zipLongest(@Nonnull InfiniteIterable iterable) {
        return () -> ZippingIterator.with(iterator(), iterable.iterator(), false);
    }
    
    /* -------------------------------------------------- Flattening -------------------------------------------------- */
    
    /**
     * Returns the elements of this iterable with all collections up to the given level flattened.
     */
    @Pure
    public <@Specifiable TYPE> @Nonnull FunctionalIterable flatten(@Positive int level);
    
    /**
     * Returns the elements of this iterable with all collections directly contained in this iterable flattened.
     */
    @Pure
    public <@Specifiable TYPE> @Nonnull FunctionalIterable flattenOne();
    
    /**
     * Returns the elements of this iterable with all collections directly or indirectly contained in this iterable flattened.
     */
    @Pure
    public <@Specifiable TYPE> @Nonnull FunctionalIterable flattenAll();
    
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy