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 :).
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);
}