Гуава: Почему нет функции Lists.filter ()?

86

Есть ли причина?

Lists.transform()

но нет

Lists.filter()

?

Как правильно отфильтровать список? Я мог бы использовать

new ArrayList(Collection2.filter())

конечно, но таким образом не гарантируется, что мой заказ останется прежним, если я правильно понимаю.

Фабиан Цайндль
источник
8
FYI, List.newArrayList (Iterables.filter (...)) обычно быстрее, чем новый ArrayList (Collection2.filter (...)). Конструктор ArrayList вызывает size () для отфильтрованной коллекции, и для вычисления размера требуется, чтобы фильтр применялся к каждому элементу исходного списка.
Джаред Леви,
4
@JaredLevy Может, вместо List.newArrayList(Iterables.filter(...))этого следует сказать Lists.newArrayList(Iterables.filter(...)) .
Abdull

Ответы:

57

Он не был реализован, потому что он предоставил бы опасное большое количество медленных методов, таких как #get (index) в возвращенном представлении списка (вызывая ошибки производительности). И ListIterator тоже было бы сложно реализовать (хотя я отправил патч несколько лет назад, чтобы покрыть это).

Поскольку индексированные методы не могут быть эффективными в представлении отфильтрованного списка, лучше просто использовать отфильтрованный Iterable, у которого их нет.

Димитрис Андреу
источник
7
Вы предполагаете, что будет возвращено представление списка. Однако #filter может быть реализован как возвращающий новый материализованный список, чего я и ожидал от метода фильтрации для списков, в отличие от метода Iterable.
Феликс Лейпольд
@FelixLeipold Однако это может замутить воду. Как это, filterпоследовательно означает вид (наряду с поведением, предполагающим) Является ли это Iterables.filter, и Sets.filterт.д. Так Iterables.filterкомбайнам легко с copyOfпо любому ImmutableCollection, я считаю , это хороший дизайн компромисс (против придумывает дополнительными методы и имена, как filteredCopyи этажерки , для комбинаций простых утилит).
Люк Ашервуд
37

Вы можете использовать Iterables.filter, что однозначно поддержит порядок.

Обратите внимание, что, создавая новый список, вы будете копировать элементы (конечно, только ссылки) - так что это не будет прямой вид на исходный список. Создание представления было бы довольно сложной задачей - рассмотрим такую ​​ситуацию:

Predicate<StringBuilder> predicate = 
    /* predicate returning whether the builder is empty */
List<StringBuilder> builders = Lists.newArrayList();
List<StringBuilder> view = Lists.filter(builders, predicate);

for (int i = 0; i < 10000; i++) {
    builders.add(new StringBuilder());
}
builders.get(8000).append("bar");

StringBuilder firstNonEmpty = view.get(0);

Это должно было бы перебрать весь исходный список, применяя фильтр ко всему. Я полагаю, может потребоваться, чтобы сопоставление предикатов не изменялось за время существования представления, но это было бы не совсем удовлетворительно.

(Это всего лишь предположение, заметьте. Возможно, кто-нибудь из разработчиков Guava объяснит настоящую причину :)

Джон Скит
источник
1
Collections2.filter.iteratorпросто звонит Iterables.filter, поэтому результат такой же.
skaffman
@skaffman: В этом случае я бы использовал Iterables.filterверсию только для ясности.
Джон Скит
3
... если вам не понадобится что- view.size()то позже в коде :)
Xaerxess
28

new List(Collection2.filter())Конечно, я мог бы использовать , но таким образом не гарантируется, что мой порядок останется прежним.

Это неправда. Collections2.filter()это функция с отложенным вычислением - она ​​фактически не фильтрует вашу коллекцию, пока вы не начнете получать доступ к отфильтрованной версии. Например, если вы перебираете отфильтрованную версию, то отфильтрованные элементы будут появляться из итератора в том же порядке, что и исходная коллекция (за вычетом отфильтрованных, очевидно).

Возможно, вы думали, что он выполняет фильтрацию заранее, а затем выгружает результаты в произвольную, неупорядоченную коллекцию некоторой формы - это не так.

Поэтому, если вы используете вывод в Collections2.filter()качестве входных данных для нового списка, то ваш исходный порядок будет сохранен.

Используя статический импорт (и Lists.newArrayListфункцию), он становится довольно лаконичным:

List filteredList = newArrayList(filter(originalList, predicate));

Обратите внимание , что в то время Collections2.filterне охотно итерация по основной коллекции, Lists.newArrayList будет - он будет извлекать все элементы отфильтрованной коллекции и скопировать их в новый ArrayList.

Скаффман
источник
Это больше похоже на: List filteredList = newArrayList(filter(originalList, new Predicate<T>() { @Override public boolean apply(T input) { return (...); } }));или т.е. List filteredList = newArrayList(filter(originalList, Predicates.notNull()));
Xaerxess
@Xaerxess: Упс, да, забыл предикат ... исправлено
skaffman
@Bozho: Спасибо ... это заняло у меня достаточно времени :)
skaffman
12

Как упоминал Джон, вы можете использовать Iterables.filter(..)или, Collections2.filter(..)и если вам не нужен просмотр в реальном времени, вы можете использовать ImmutableList.copyOf(Iterables.filter(..))или, Lists.newArrayList( Iterables.filter(..))и да, порядок будет сохранен.

Если вас действительно интересует, зачем нужна часть, вы можете посетить https://github.com/google/guava/issues/505 для получения дополнительных сведений.

Премрадж
источник
6

Подводя итог тому, что сказали другие, вы можете легко создать общую оболочку для фильтрации списков:

public static <T> List<T> filter(Iterable<T> userLists, Predicate<T> predicate) {
    return Lists.newArrayList(Iterables.filter(userLists, predicate));
}
Хольгер Брандл
источник