Proper API for Java List

Sergiy Yevtushenko - Oct 28 '19 - - Dev Community

Java Collection Framework is quite old. First appeared in Java 1.2 and since then it changed not so much. It reflects OO-concepts as they were understood by the end of 20th century.

Since then many things changed and new understanding grew of what is actually needed from List, Set, Map, etc. API's. Much more often we're using immutable collections and many of us realize that we need completely different API's from one provided with standard JDK classes.

This article is dedicated to Lists, so I propose to discuss how ideal API for immutable Java List implementation should look like.

From my experience I've tried to prepare version of the API which should be more convenient to use:

public interface List<E> {
    /**
     * Return first element from list.
     * 
     * @return First element wrapped into {@link Option} if element is present or {@link Option#empty()} otherwise
     */
    Option<E> first();

    /**
     * Return first {@code N} elements from the list. If there are less elements than requested, then avalable
     * elements returned.
     * @param n
     *        Number of elements to return.
     *        
     * @return New list with at most requested number of elements
     */
    List<E> first(final int n);

    /**
     * Return last element from list.
     *
     * @return Last element wrapped into {@link Option} if element is present or {@link Option#empty()} otherwise
     */
    Option<E> last();

    /**
     * Return list which contains elements from current list followed by elements from list provided as parameter.
     * 
     * @param other
     *        List to append
     *        
     * @return List with elements from current list followed by elements from {@code other} list
     */
    List<E> append(final List<E> other);

    /**
     * Return list which contains elements from list provided as parameter followed by elements of current list.
     * 
     * @param other
     *        List to append to
     *        
     * @return List with elements from {@code other} list followed by elements from current list
     */
    List<E> prepend(final List<E> other);

    /**
     * Return list consisting of elements obtained from elements of current list with applied 
     * transformation function.
     * 
     * @param mapper
     *        Transformation function
     *        
     * @return New list with transformed elements
     */
    <R> List<R> map(final FN1<R, E> mapper);

    /**
     * Return list consisting of elements obtained from elements of current list with applied 
     * transformation function. Unlike {@link #map(FN1)} this method passes index of element
     * along with element to transformation function.
     *
     * @param mapper
     *        Transformation function
     *        
     * @return New list with transformed elements
     */
    <R> List<R> mapN(final FN2<R, Integer, E> mapper);

    /**
     * Applies specified consumer to elements of current list.
     * 
     * @param consumer
     *        Consumer for elements
     *        
     * @return Current list  
     */
    List<E> apply(final Consumer<E> consumer);

    /**
     * Applies specified consumer to elements of current list. 
     * Unlike {@link #apply(Consumer)} element index is passed along with element to consumer.
     * 
     * @param consumer
     *        Consumer for elements
     *        
     * @return Current list
     */
    List<E> applyN(final BiConsumer<Integer, E> consumer);

    /**
     * Create {@link Stream} from list elements.
     * 
     * @return Created stream
     */
    Stream<E> stream();

    /**
     * Return list size.
     * 
     * @return number of elements in list
     */
    int size();

    /**
     * Create new list which will hold the same elements but sorted according to 
     * provided comparator.
     * 
     * @param comparator
     *        Element comparator
     *        
     * @return Sorted list
     */
    List<E> sort(final Comparator<E> comparator);

    /**
     * Create new list which will hold only elements which satisfy provided predicate.
     * 
     * @param predicate
     *        Predicate to apply to elements
     * 
     * @return List of elements for which predicate returned {@code true}
     */
    List<E> filter(final Predicate<E> predicate);

    /**
     * Split current list into two using provided predicate. Result is a pair of lists. Left element of pair
     * contains list with elements evaluated to {@code false} by predicate. Right element of pair contains
     * list with elements evaluated to {@code true} by predicate.
     * 
     * @param predicate
     *        Predicate to apply to elements
     * 
     * @return Pair of lists with results 
     */
    Pair<List<E>, List<E>> splitBy(final Predicate<E> predicate);
}

Enter fullscreen mode Exit fullscreen mode

Beside listed above API's there should be factory methods and Collector implementation which should enable convenient interaction of Stream API, but that part is more or less clear.

I'd be happy to get feedback on the List API proposed above and get any comments/ideas/suggestions/considerations in regard to it.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Terabox Video Player