Гуава: Почему нет функции Lists.filter ()? - PullRequest
86 голосов
/ 10 декабря 2011

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

Lists.transform()

, но нет

Lists.filter()

?

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

new ArrayList(Collection2.filter())

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

Ответы [ 5 ]

57 голосов
/ 11 декабря 2011

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

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

37 голосов
/ 10 декабря 2011

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

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

(Это просто предположение, заметьте.сопровождающие скинутся по реальной причине:)

28 голосов
/ 11 декабря 2011

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

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

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

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

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

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

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

12 голосов
/ 11 декабря 2011

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

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

6 голосов
/ 16 сентября 2014

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

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