Если вы заботитесь об эффективности, вам следует избегать повторяющихся одиночных операций remove
, особенно в начале List
, так как наиболее часто используемая реализация, ArrayList
, не очень хорошо работает, так какПри удалении записи необходимо скопировать все оставшиеся элементы в его внутреннем массиве.
Наихудший случай, т.е. удаление всех элементов таким образом, будет иметь квадратичную сложность по времени.
public static <T> List<T> trim(List<T> list, Predicate<T> trimPredicate) {
Objects.requireNonNull(list, "list is null");
Objects.requireNonNull(trimPredicate, "trimPredicate is null");
int lastMatch;
for(ListIterator<T> it = list.listIterator(lastMatch = list.size());
it.hasPrevious() && trimPredicate.test(it.previous());) lastMatch = it.nextIndex();
if(lastMatch < list.size()) list.subList(lastMatch, list.size()).clear();
for(ListIterator<T> it = list.listIterator(lastMatch = 0);
it.hasNext() && trimPredicate.test(it.next()); ) lastMatch = it.previousIndex();
if(lastMatch > 0) list.subList(0, lastMatch+1).clear();
return list;
}
List.subList(…).clear()
- это правильная идиома для эффективного удаления ряда предметов.В случае ArrayList
это подразумевает только одну операцию копирования для удаления всего диапазона.Таким образом, мы только выполняем итерацию без удаления, чтобы определить диапазон, а затем выполняем одну операцию удаления.
Поскольку удаление в конце не требует дополнительных затрат, так как нет оставшихся элементов для копирования, это решение сначала удаляет совпаденияв конце, чтобы потенциально уменьшить количество оставшихся элементов для последующего удаления совпадений в начале списка.
Для операции на месте, которая будет непосредственно изменять список, нет потокарешение, которое улучшит его.
Даже если вы хотите вернуть новый список, одно из самых эффективных решений будет основано на subList
:
public static <T> List<T> trim(List<T> list, Predicate<T> trimPredicate) {
Objects.requireNonNull(list, "list is null");
Objects.requireNonNull(trimPredicate, "trimPredicate is null");
int firstToKeep = 0, lastToKeep = list.size();
for(T t: list) if(trimPredicate.test(t)) firstToKeep++; else break;
for(ListIterator<T> it = list.listIterator(lastToKeep);
lastToKeep > firstToKeep && it.hasPrevious() && trimPredicate.test(it.previous());)
lastToKeep = it.nextIndex();
return new ArrayList<>(list.subList(firstToKeep, lastToKeep));
}