Guava: Почему нет функции List.filter()?

Есть ли причина, по которой есть

Lists.transform()

но не

Lists.filter()

?

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

new ArrayList(Collection2.filter())

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

Ответ 1

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

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

Ответ 2

Вы можете использовать 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);

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

(Это просто угадайте, заметьте. Возможно, один из сопровождающих Гуавы побьет настоящую причину:)

Ответ 3

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

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

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

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

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

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

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

Ответ 4

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

Если вы действительно заинтересованы в том, почему часть, вы можете посетить http://code.google.com/p/guava-libraries/issues/detail?id=505 для более подробной информации.

Ответ 5

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

public static <T> List<T> filter(Iterable<T> userLists, Predicate<T> predicate) {
    return Lists.newArrayList(Iterables.filter(userLists, predicate));
}