Search code examples
javajava-streamtrim

Java stream trim list


I have the following implementation for trimming a list of elements based on a given predicate (that is - remove leading and trailing empty elements).

I would like to make the implementation more readable, preferably using Java's stream API's.

/**
 * Trim a List based on a given predicate, that is, remove leading
 * and trailing elements that match the predicate (but not in-between
 * non-matching elements).
 *
 * @param list the list to trim
 * @param trimPredicate the predicate for trimming
 * @param <T> type of the list
 * @return the same list minus the trimmed elements.
 * @throws NullPointerException if the list is {@code null}
 * @throws UnsupportedOperationException if the {@code remove}
 *      operation is not supported by the list iterator
 */
public static <T> List<T> trim(List<T> list, Predicate<T> trimPredicate)
        throws NullPointerException, UnsupportedOperationException {
    if (list == null) throw new NullPointerException("list is null");
    ListIterator<T> it = list.listIterator();
    while (it.hasNext() && trimPredicate.test(it.next())) it.remove();
    it = list.listIterator(list.size());
    while (it.hasPrevious() && trimPredicate.test(it.previous())) it.remove();

    return list;
}

Any suggestions?

An example, to make things clearer:

For List<Integer>, considering 0 to be an empty value, the following list:

[0, 0, 3, 5, 0, 4, 0, -3, 0, 0]

Will be trimmed to:

[3, 5, 0, 4, 0, -3]

(And the fact that at least two different readers here got it wrong, demonstrates my point regarding the code's readability :).


Solution

  • your original code is pretty readable and efficient. recommended.

    static <T> List<T> trim2(List<T> list, Predicate<T> isEmpty) {
        ListIterator<T> it = list.listIterator();
        while (it.hasNext() && isEmpty.test(it.next())) {
            it.remove();
        }
        it = list.listIterator(list.size());
        while (it.hasPrevious() && isEmpty.test(it.previous())) {
            it.remove();
        }
        return list;
    }
    

    stream version using java 9. just for the heck of it, not recommended.

    static <T> List<T> trim3(List<T> list, Predicate<T> isEmpty) {
        Collection<T> ltrimreverse = list.stream().dropWhile(isEmpty)
            .collect(ArrayDeque::new, ArrayDeque::push, ArrayDeque::addAll);
        Collection<T> rtrim = ltrimreverse.stream().dropWhile(isEmpty)
            .collect(ArrayDeque::new, ArrayDeque::push, ArrayDeque::addAll);
        return new ArrayList<>(rtrim);
    }