Как лучше всего фильтровать коллекцию Java?

Я хочу отфильтровать java.util.Collection на основе предиката.

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
698
0
662 934
27
Перейти к ответу Данный вопрос помечен как решенный

Ответы 27

Используйте CollectionUtils.filter (Коллекция, Предикат) из Apache Commons.

это нормально, но не является универсальным и изменяет коллекцию на месте (нехорошо)

Kevin Wong 23.09.2008 20:30

В CollectionUtils есть и другие методы фильтрации, которые не изменяют исходную коллекцию.

skaffman 06.09.2009 19:08

В частности, метод, который нет изменяет коллекцию на месте, - это org.apache.commons.collections.CollectionUtils # select (Collection, Predicate)

Eero 29.09.2010 16:27

В Commons Collections v4 теперь используются Generics.

Justin Emery 16.06.2014 23:26

Этот метод следует использовать с осторожностью, поскольку он полагается (по крайней мере, в реализации commons-collections-3.2.1) на метод iterator.remove (), который является необязательным для коллекций, поэтому вместо фильтрации, скажем, массива вы можете получить исключение UnsupportedOperationException.

user2417480 15.10.2015 15:18

Рассмотрим Коллекции Google для обновленной структуры коллекций, которая поддерживает универсальные шаблоны.

ОБНОВИТЬ: библиотека коллекций Google устарела. Вместо этого вы должны использовать последнюю версию Гуава. Он по-прежнему имеет все те же расширения структуры коллекций, включая механизм фильтрации на основе предиката.

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

Kevin Wong 23.09.2008 22:25

Kevin, Iterables.filter () и Iterators.filter () были там с самого начала, и обычно это все, что вам нужно.

Kevin Bourrillion 08.11.2009 19:43

Вы уверены, что хотите отфильтровать саму Коллекцию, а не итератор?

см. org.apache.commons.collections.iterators.FilterIterator

или используя версию 4 apache commons org.apache.commons.collections4.iterators.FilterIterator

Настройка:

public interface Predicate<T> {
  public boolean filter(T t);
}

void filterCollection(Collection<T> col, Predicate<T> predicate) {
  for (Iterator i = col.iterator(); i.hasNext();) {
    T obj = i.next();
    if (predicate.filter(obj)) {
      i.remove();
    }
  }
}

Использование:

List<MyObject> myList = ...;
filterCollection(myList, new Predicate<MyObject>() {
  public boolean filter(MyObject obj) {
    return obj.shouldFilter();
  }
});

Хорошо, но я предпочитаю реализацию Alan, потому что вы получаете копию коллекции вместо ее изменения. Более того, код Алана является потокобезопасным, а ваш - нет.

marcospereira 24.09.2008 07:45

"Лучший" способ - это слишком широкая просьба. Он самый "короткий"? «Самый быстрый»? "Удобочитаемый"? Фильтр на месте или в другую коллекцию?

Самый простой (но не самый читаемый) способ - перебрать его и использовать метод Iterator.remove ():

Iterator<Foo> it = col.iterator();
while( it.hasNext() ) {
  Foo foo = it.next();
  if ( !condition(foo) ) it.remove();
}

Теперь, чтобы сделать его более читабельным, вы можете обернуть его в служебный метод. Затем изобрести интерфейс IPredicate, создать анонимную реализацию этого интерфейса и сделать что-нибудь вроде:

CollectionUtils.filterInPlace(col,
  new IPredicate<Foo>(){
    public boolean keepIt(Foo foo) {
      return foo.isBar();
    }
  });

где filterInPlace () выполняет итерацию по коллекции и вызывает Predicate.keepIt (), чтобы узнать, следует ли сохранить экземпляр в коллекции.

Я действительно не вижу оправдания для использования сторонней библиотеки только для этой задачи.

Я голосую за это: он просто работает, без внешних библиотек. Я никогда не понимал, что создание экземпляра Iterator может быть действительно полезным по сравнению с использованием синтаксиса for-each или что вы можете удалять элементы из списка без ConcurrentModificationException или чего-то в этом роде. :)

ZeroOne 21.12.2012 15:40

Думаю, это лучший способ использовать стандартную библиотеку Java без копирования. В версии 1.8 будет функция stream(), но не все смогут играть с новейшими игрушками: P

Populus 12.12.2014 18:38

Это тоже изменяет исходную коллекцию? @ZeroOne

Rohan 15.03.2016 05:46

Да, конечно, @Rohan. Попробуйте, если не верите. ;)

ZeroOne 15.03.2016 06:39

Ха-ха, я сделал! Но я хочу сохранить свою оригинальную коллекцию. Можете ли вы предложить способ сделать это без добавления внешней библиотеки? @ZeroOne

Rohan 15.03.2016 07:13

@ Рохан, хорошо, значит, ты хочешь новый список? Для этого см. Ответ gavenkoa с Java 8.

ZeroOne 15.03.2016 09:38

Предполагая, что вы используете Java 1.5 и не можете добавить Коллекции Google, я бы сделал что-то очень похожее на то, что сделали ребята из Google. Это небольшая вариация комментариев Джона.

Сначала добавьте этот интерфейс в свою кодовую базу.

public interface IPredicate<T> { boolean apply(T type); }

Его разработчики могут ответить, когда определенный предикат истинен для определенного типа. Например. Если T был User, а AuthorizedUserPredicate<User> реализует IPredicate<T>, то AuthorizedUserPredicate#apply возвращает, авторизован ли переданный в User.

Тогда в каком-нибудь служебном классе вы могли бы сказать

public static <T> Collection<T> filter(Collection<T> target, IPredicate<T> predicate) {
    Collection<T> result = new ArrayList<T>();
    for (T element: target) {
        if (predicate.apply(element)) {
            result.add(element);
        }
    }
    return result;
}

Итак, предполагая, что у вас есть использование вышеперечисленного, может быть

Predicate<User> isAuthorized = new Predicate<User>() {
    public boolean apply(User user) {
        // binds a boolean method in User to a reference
        return user.isAuthorized();
    }
};
// allUsers is a Collection<User>
Collection<User> authorizedUsers = filter(allUsers, isAuthorized);

Если производительность при линейной проверке вызывает беспокойство, тогда мне может потребоваться объект домена с целевой коллекцией. Объект домена, который имеет целевую коллекцию, будет иметь логику фильтрации для методов, которые инициализируют, добавляют и устанавливают целевую коллекцию.

ОБНОВИТЬ:

В служебном классе (скажем, Predicate) я добавил метод выбора с опцией для значения по умолчанию, когда предикат не возвращает ожидаемое значение, а также статическое свойство для параметров, которые будут использоваться внутри нового IPredicate.

public class Predicate {
    public static Object predicateParams;

    public static <T> Collection<T> filter(Collection<T> target, IPredicate<T> predicate) {
        Collection<T> result = new ArrayList<T>();
        for (T element : target) {
            if (predicate.apply(element)) {
                result.add(element);
            }
        }
        return result;
    }

    public static <T> T select(Collection<T> target, IPredicate<T> predicate) {
        T result = null;
        for (T element : target) {
            if (!predicate.apply(element))
                continue;
            result = element;
            break;
        }
        return result;
    }

    public static <T> T select(Collection<T> target, IPredicate<T> predicate, T defaultValue) {
        T result = defaultValue;
        for (T element : target) {
            if (!predicate.apply(element))
                continue;
            result = element;
            break;
        }
        return result;
    }
}

В следующем примере выполняется поиск отсутствующих объектов между коллекциями:

List<MyTypeA> missingObjects = (List<MyTypeA>) Predicate.filter(myCollectionOfA,
    new IPredicate<MyTypeA>() {
        public boolean apply(MyTypeA objectOfA) {
            Predicate.predicateParams = objectOfA.getName();
            return Predicate.select(myCollectionB, new IPredicate<MyTypeB>() {
                public boolean apply(MyTypeB objectOfB) {
                    return objectOfB.getName().equals(Predicate.predicateParams.toString());
                }
            }) == null;
        }
    });

В следующем примере выполняется поиск экземпляра в коллекции и возврат первого элемента коллекции в качестве значения по умолчанию, если экземпляр не найден:

MyType myObject = Predicate.select(collectionOfMyType, new IPredicate<MyType>() {
public boolean apply(MyType objectOfMyType) {
    return objectOfMyType.isDefault();
}}, collectionOfMyType.get(0));

ОБНОВЛЕНИЕ (после выпуска Java 8):

Прошло несколько лет с тех пор, как я (Алан) впервые опубликовал этот ответ, и я до сих пор не могу поверить, что собираю ТАК баллов за этот ответ. Во всяком случае, теперь, когда Java 8 ввела в язык замыкания, мой ответ был бы значительно другим и более простым. В Java 8 нет необходимости в отдельном статическом служебном классе. Итак, если вы хотите найти 1-й элемент, соответствующий вашему предикату.

final UserService userService = ... // perhaps injected IoC
final Optional<UserModel> userOption = userCollection.stream().filter(u -> {
    boolean isAuthorized = userService.isAuthorized(u);
    return isAuthorized;
}).findFirst();

JDK 8 API для дополнительных компонентов поддерживает get(), isPresent(), orElse(defaultUser), orElseGet(userSupplier) и orElseThrow(exceptionSupplier), а также другие «монадические» функции, такие как map, flatMap и filter.

Если вы хотите просто собрать всех пользователей, которые соответствуют предикату, используйте Collectors для завершения потока в желаемой коллекции.

final UserService userService = ... // perhaps injected IoC
final List<UserModel> userOption = userCollection.stream().filter(u -> {
    boolean isAuthorized = userService.isAuthorized(u);
    return isAuthorized;
}).collect(Collectors.toList());

См. здесь для получения дополнительных примеров того, как работают потоки Java 8.

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

Kevin Wong 25.09.2008 22:18

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

Josh 11.10.2008 23:54

@Nestor: в понимании Scala фильтрация была бы намного проще: val authorized = for (user <- users if user.isAuthorized) yield user

Alan 04.06.2014 00:33

Это изменит исходную коллекцию или создаст новую? Я попытался использовать этот метод и зарегистрировал обе свои коллекции (исходную и возвращенную методом), они одинаковы. @Алан

Rohan 14.03.2016 15:45

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

Rohan 15.03.2016 05:44

@Rohan, это не предназначено для изменения исходной коллекции. Обратите внимание, что приведенная выше коллекция результатов создается заново, и метод фильтра добавляет в нее только в том случае, если применяется предикат.

Alan 11.06.2016 19:04

Метод Collections2.filter (Коллекция, Предикат) в Библиотека Google Guava делает именно то, что вы ищете.

С помощью ForEach DSL вы можете написать

import static ch.akuhn.util.query.Query.select;
import static ch.akuhn.util.query.Query.$result;
import ch.akuhn.util.query.Select;

Collection<String> collection = ...

for (Select<String> each : select(collection)) {
    each.yield = each.value.length() > 3;
}

Collection<String> result = $result();

Учитывая набор [Быстрый, коричневый, лиса, прыжки, за, ленивый, собака], это приводит к [быстро, коричневый, прыжки, за, ленивый], то есть все строки длиннее трех символов.

Все стили итерации, поддерживаемые ForEach DSL, являются

  • AllSatisfy
  • AnySatisfy
  • Collect
  • Counnt
  • CutPieces
  • Detect
  • GroupedBy
  • IndexOf
  • InjectInto
  • Reject
  • Select

Для получения более подробной информации, пожалуйста, обратитесь к https://www.iam.unibe.ch/scg/svn_repos/Sources/ForEach

Это довольно умно! Однако предстоит проделать большую работу по реализации красивого синтаксиса в стиле Ruby! Отрицательным является то, что ваш фильтр не является первоклассной функцией и, следовательно, не может быть использован повторно. Закатывающиеся застежки ...

oxbow_lakes 26.02.2009 00:47

Хорошая точка зрения. Один из способов повторно использовать тело цикла - преобразовать цикл в метод, который принимает запрос выбора в качестве параметра. Однако это, конечно, далеко не так удобно и мощно, как настоящие закрытия.

akuhn 28.02.2009 19:00
Ответ принят как подходящий

Java 8 (2014 г.) решает эту проблему, используя потоки и лямбды в одной строке кода:

List<Person> beerDrinkers = persons.stream()
    .filter(p -> p.getAge() > 16).collect(Collectors.toList());

Вот руководство.

Используйте Collection#removeIf, чтобы изменить коллекцию на месте. (Примечание: в этом случае предикат удалит объекты, удовлетворяющие этому предикату):

persons.removeIf(p -> p.getAge() <= 16);

лямбдаж позволяет фильтровать коллекции без написания циклов или внутренних классов:

List<Person> beerDrinkers = select(persons, having(on(Person.class).getAge(),
    greaterThan(16)));

Вы можете представить что-нибудь более читабельное?

Заявление об ограничении ответственности: Я участник lambdaj

Приятно, но статический импорт запутывает происходящее. Для справки выберите / имеющий / включен статический импорт на ch.lambdaj.Lambda, больше чем org.hamcrest.Matchers

MikePatel 15.03.2012 15:57

LambdaJ действительно привлекательный, но стоит отметить, что он подразумевает значительные накладные расходы (в среднем 2,6): code.google.com/p/lambdaj/wiki/PerformanceAnalysis.

Doc Davluz 10.04.2012 15:54

Видимо не работает на Android: groups.google.com/forum/#!msg/lambdaj/km7uFgvSd3k/grJhgl3ik5‌ sJ

Moritz 17.05.2013 13:37

Очень нравится этот пример LamdaJ ... похожий на встроенные в .NET лямбда-функции. А где пить в 16 лет? Нам следует подумать о добавлении ограничения локализации. :П

MAbraham1 16.08.2013 18:16

Бог! Я ненавижу эту утечку с Lambdaj, теперь, когда я использую Java 8, я ищу удаление каждого использования Lambdaj

Nestor Hernandez Loli 20.07.2014 07:09

Я попытался включить Lambdaj, но это было слишком сложно и было нечитаемо для людей, которые не знали Lambdaj. Хотя это только мой опыт.

Evgeni Petrov 08.11.2014 13:50

Жаль, что он не может использовать перечисления. Я перешел на Apache Commons.

Ronen Festinger 01.09.2015 01:37

Этот ответ был бы даже лучше, если бы он также упомянул простейший метод (начиная с Java 8): Collection.removeIf

Lii 24.10.2015 16:08

Я отредактировал ваш ответ, включив в него Collection#removeIf. Надеюсь, ты не против!

Alexis C. 09.12.2015 03:25

Интересно, почему верхняя половина не была дана в качестве нового ответа. Если бы это прошло сегодня через конвейер рецензирования, правка, вероятно, была бы отклонена. Сколько голосов за исходный вопрос и сколько за редактирование?

demongolem 25.12.2016 07:22

removeЕсли пример должен быть persons.removeIf(p -> p.getAge() <= 16);

vim 09.02.2017 17:18

Внимание: если вы используете лямбды внутри класса сущности Eclipselink (<2.7), это приведет к странным исключениям.

Oscar Pérez 25.05.2018 13:32

Java (8) наконец догнала dotNet Linq. Ура! # circa2007 en.wikipedia.org/wiki/Language_Integrated_Query

granadaCoder 04.10.2018 18:02

что это за перфорация? кто нибудь проверял?

Deian 06.10.2018 08:05
Вы можете представить что-нибудь более читабельное? -- have you checked out Kotlin? ;) persons.filter { it.age >= 16 }
TheOperator 30.04.2019 14:39

В C# это просто persons.Where(p => p.getAge() > 16).ToList(). Любая идея, почему Java требует шаблонов, таких как stream, Collectors.toList и т. д.?

nawfal 24.06.2020 21:10
Для вызова требуется уровень API 24 (текущий мин - 19): `java.util.Collection # removeIf
Iman Marashi 31.10.2020 12:44

Это, в сочетании с отсутствием реальных закрытий, является моей самой большой проблемой для Java. Честно говоря, большинство упомянутых выше методов довольно легко читаются и ДЕЙСТВИТЕЛЬНО эффективны; однако, проведя время с .Net, Erlang и т. д., понимание списков, интегрированное на уровне языка, делает все намного чище. Без дополнений на уровне языка Java не может быть такой же чистой, как многие другие языки в этой области.

Если производительность является огромной проблемой, можно использовать коллекции Google (или напишите свою собственную простую утилиту предикатов). Синтаксис Lambdaj более читабелен для некоторых людей, но не так эффективен.

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

LinkedList<Person> list = ......
LinkedList<Person> filtered = 
           Query.from(list).where(Condition.ensure("age", Op.GTE, 21));

ИЛИ ЖЕ

LinkedList<Person> list = ....
LinkedList<Person> filtered = Query.from(list).where("x => x.age >= 21");

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

MatrixFrog 21.06.2011 02:39

Сделал репо публичным (net-machine.com/indefero/p/jdclib/source/tree/master). Вас интересует пакет выражений. В тестовом пакете есть тестер с примером использования. Я никогда особо не работал над интерфейсом строкового запроса, о котором говорилось выше (не хотелось писать настоящий синтаксический анализатор), поэтому явный интерфейс запроса в тестере - лучший вариант.

jdc0589 23.06.2011 07:02

Я написал расширенный класс Iterable, который поддерживает применение функциональных алгоритмов без копирования содержимого коллекции.

Использование:

List<Integer> myList = new ArrayList<Integer>(){ 1, 2, 3, 4, 5 }

Iterable<Integer> filtered = Iterable.wrap(myList).select(new Predicate1<Integer>()
{
    public Boolean call(Integer n) throws FunctionalException
    {
        return n % 2 == 0;
    }
})

for( int n : filtered )
{
    System.out.println(n);
}

Приведенный выше код фактически выполнит

for( int n : myList )
{
    if ( n % 2 == 0 ) 
    {
        System.out.println(n);
    }
}

JFilter http://code.google.com/p/jfilter/ лучше всего подходит для ваших требований.

JFilter - это простая и высокопроизводительная библиотека с открытым исходным кодом для запроса коллекции компонентов Java.

Ключевая особенность

  • Поддержка свойств коллекции (java.util.Collection, java.util.Map и Array).
  • Поддержка сбора внутри коллекции любой глубины.
  • Поддержка внутренних запросов.
  • Поддержка параметризованных запросов.
  • Может фильтровать 1 миллион записей за несколько 100 мс.
  • Фильтр (запрос) задается в простом формате json, он похож на запросы Mangodb. Ниже приведены некоторые примеры.
  • {"id": {"$ le": "10"}
    • где свойство id объекта меньше 10.
  • {"id": {"$ in": ["0", "100"]}}
    • где свойство id объекта равно 0 или 100.
  • {"lineItems": {"lineAmount": "1"}}
    • где свойство коллекции lineItems параметризованного типа имеет значение lineAmount, равное 1.
  • {"$ and": [{"id": "0"}, {"billingAddress": {"city": "DEL"}}]}
    • где свойство id равно 0, а свойство billingAddress.city - DEL.
  • {"lineItems": {"Tax": {"key": {"code": "GST"}, "value": {"$ gt": "1.01"}}}}
    • где свойство коллекции lineItems параметризованного типа, которое имеет свойство типа карты налогов параметризованного типа, имеет код, равный значению GST больше 1.01.
  • {'$ or': [{'code': '10'}, {'skus': {'$ and': [{'price': {'$ in': ['20', '40']}) }, {'code': 'RedApple'}]}}]}
    • Выберите все продукты, у которых код продукта - 10, цена - 20 и 40, а код - RedApple.

Вы должны отказаться от того, что являетесь автором (как я думаю).

assylias 05.04.2012 15:01

Да, я являюсь автором этой библиотеки.

Kamran Ali Khan 06.04.2012 09:50

Используйте Механизм сбора запросов (CQEngine). На сегодняшний день это самый быстрый способ сделать это.

См. Также: Как вы запрашиваете коллекции объектов в Java (критерии / SQL-подобные)?

Давайте посмотрим, как отфильтровать встроенный список JDK и MutableList с помощью Коллекции Eclipse.

List<Integer> jdkList = Arrays.asList(1, 2, 3, 4, 5);
MutableList<Integer> ecList = Lists.mutable.with(1, 2, 3, 4, 5);

Если вы хотите отфильтровать числа меньше 3, вы ожидаете следующих результатов.

List<Integer> selected = Lists.mutable.with(1, 2);
List<Integer> rejected = Lists.mutable.with(3, 4, 5);

Вот как можно фильтровать, используя лямбда Java 8 в качестве Predicate.

Assert.assertEquals(selected, Iterate.select(jdkList, each -> each < 3));
Assert.assertEquals(rejected, Iterate.reject(jdkList, each -> each < 3));

Assert.assertEquals(selected, ecList.select(each -> each < 3));
Assert.assertEquals(rejected, ecList.reject(each -> each < 3));

Вот как вы можете фильтровать, используя анонимный внутренний класс как Predicate.

Predicate<Integer> lessThan3 = new Predicate<Integer>()
{
    public boolean accept(Integer each)
    {
        return each < 3;
    }
};

Assert.assertEquals(selected, Iterate.select(jdkList, lessThan3));
Assert.assertEquals(selected, ecList.select(lessThan3));

Вот несколько альтернатив фильтрации списков JDK и Коллекции Eclipse MutableLists с использованием фабрики Предикаты.

Assert.assertEquals(selected, Iterate.select(jdkList, Predicates.lessThan(3)));
Assert.assertEquals(selected, ecList.select(Predicates.lessThan(3)));

Вот версия, которая не выделяет объект для предиката, используя вместо этого фабрику Предикаты2 с методом selectWith, который принимает Predicate2.

Assert.assertEquals(
    selected, ecList.selectWith(Predicates2.<Integer>lessThan(), 3));

Иногда вам нужно отфильтровать отрицательное условие. Для этого в Eclipse Collections есть специальный метод, называемый reject.

Assert.assertEquals(rejected, Iterate.reject(jdkList, lessThan3));
Assert.assertEquals(rejected, ecList.reject(lessThan3));

Метод partition вернет две коллекции, содержащие элементы, выбранные и отклоненные Predicate.

PartitionIterable<Integer> jdkPartitioned = Iterate.partition(jdkList, lessThan3);
Assert.assertEquals(selected, jdkPartitioned.getSelected());
Assert.assertEquals(rejected, jdkPartitioned.getRejected());

PartitionList<Integer> ecPartitioned = gscList.partition(lessThan3);
Assert.assertEquals(selected, ecPartitioned.getSelected());
Assert.assertEquals(rejected, ecPartitioned.getRejected());

Примечание: я являюсь приверженцем коллекций Eclipse.

Как бы вы включили removeIf в список или набор для примитивов?

Vivek Rao 17.10.2017 00:03

API для removeIf был добавлен к примитивным коллекциям в EC 9.1. eclipse.org/collections/javadoc/9.1.0/org/eclipse/collection‌ s /…

Donald Raab 01.02.2020 10:55

Дождитесь Java 8:

List<Person> olderThan30 = 
  //Create a Stream from the personList
  personList.stream().
  //filter the element to select only those with age >= 30
  filter(p -> p.age >= 30).
  //put those filtered elements into a new List.
  collect(Collectors.toList());

Ух ... это так многословно. Почему они не могли просто сделать: List <Person> result = personList.filter (p -> p.age> 30);

Kevin Wong 30.08.2013 17:38

@KevinWong Я действительно получил пример из официального руководства, поэтому не знаю другого синтаксиса ...

gavenkoa 30.08.2013 17:42

Не сомневаюсь, что это официальный синтаксис. Я просто говорю, что это отстой.

Kevin Wong 06.09.2013 22:58

Чтобы использовать фильтр непосредственно на Коллекция, вам необходимо использовать вызов removeIf: download.java.net/jdk8/docs/api/java/util/…

gavenkoa 07.09.2013 17:55

@KevinWong "многословный" в значительной степени описывает весь язык, я думаю. По крайней мере, они последовательны?

Rogue 12.05.2014 09:09

Почему бы не использовать Collectors.toList () в последней части?

Nestor Hernandez Loli 12.05.2014 17:45
Здесь is a link gavenkoa provided that doesn't 404. personList.removeIf(p -> p.age < 30); Less verbose. Also, I've heard talk about starting to implement apis that accept and return Streams rather than Collections because Streams are very useful and fast but going to/from them is slow.
Captain Man 16.06.2015 23:58

Начиная с раннего выпуска Java 8, вы могли попробовать что-то вроде:

Collection<T> collection = ...;
Stream<T> stream = collection.stream().filter(...);

Например, если у вас есть список целых чисел, и вы хотите отфильтровать числа, которые больше 10, а затем распечатать эти числа на консоли, вы можете сделать что-то вроде:

List<Integer> numbers = Arrays.asList(12, 74, 5, 8, 16);
numbers.stream().filter(n -> n > 10).forEach(System.out::println);

Как насчет простой и понятной Java

 List<Customer> list ...;
 List<Customer> newList = new ArrayList<>();
 for (Customer c : list){
    if (c.getName().equals("dd")) newList.add(c);
 }

Просто, читабельно и легко (и работает в Android!) Но если вы используете Java 8, вы можете сделать это одной милой строкой:

List<Customer> newList = list.stream().filter(c -> c.getName().equals("dd")).collect(toList());

Обратите внимание, что toList () импортируется статически

Простое решение до Java8:

ArrayList<Item> filtered = new ArrayList<Item>(); 
for (Item item : items) if (condition(item)) filtered.add(item);

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

Закину в кольцо RxJava, который тоже есть на Android. RxJava не всегда может быть лучшим вариантом, но он даст вам больше гибкости, если вы захотите добавить больше преобразований в свою коллекцию или обработать ошибки при фильтрации.

Observable.from(Arrays.asList(1, 2, 3, 4, 5))
    .filter(new Func1<Integer, Boolean>() {
        public Boolean call(Integer i) {
            return i % 2 != 0;
        }
    })
    .subscribe(new Action1<Integer>() {
        public void call(Integer i) {
            System.out.println(i);
        }
    });

Выход:

1
3
5

Более подробную информацию о filter RxJava можно найти в здесь.

https://code.google.com/p/joquery/

Поддерживает разные возможности,

Данная коллекция,

Collection<Dto> testList = new ArrayList<>();

типа,

class Dto
{
    private int id;
    private String text;

    public int getId()
    {
        return id;
    }

    public int getText()
    {
        return text;
    }
}

Фильтр

Java 7

Filter<Dto> query = CQ.<Dto>filter(testList)
    .where()
    .property("id").eq().value(1);
Collection<Dto> filtered = query.list();

Java 8

Filter<Dto> query = CQ.<Dto>filter(testList)
    .where()
    .property(Dto::getId)
    .eq().value(1);
Collection<Dto> filtered = query.list();

Также,

Filter<Dto> query = CQ.<Dto>filter()
        .from(testList)
        .where()
        .property(Dto::getId).between().value(1).value(2)
        .and()
        .property(Dto::grtText).in().value(new string[]{"a","b"});

Сортировка (также доступно для Java 7)

Filter<Dto> query = CQ.<Dto>filter(testList)
        .orderBy()
        .property(Dto::getId)
        .property(Dto::getName)
    Collection<Dto> sorted = query.list();

Группировка (также доступно для Java 7)

GroupQuery<Integer,Dto> query = CQ.<Dto,Dto>query(testList)
        .group()
        .groupBy(Dto::getId)
    Collection<Grouping<Integer,Dto>> grouped = query.list();

Присоединяется (также доступно для Java 7)

Данный,

class LeftDto
{
    private int id;
    private String text;

    public int getId()
    {
        return id;
    }

    public int getText()
    {
        return text;
    }
}

class RightDto
{
    private int id;
    private int leftId;
    private String text;

    public int getId()
    {
        return id;
    }

    public int getLeftId()
        {
            return leftId;
        }

    public int getText()
    {
        return text;
    }
}

class JoinedDto
{
    private int leftId;
    private int rightId;
    private String text;

    public JoinedDto(int leftId,int rightId,String text)
    {
        this.leftId = leftId;
        this.rightId = rightId;
        this.text = text;
    }

    public int getLeftId()
    {
        return leftId;
    }

    public int getRightId()
        {
            return rightId;
        }

    public int getText()
    {
        return text;
    }
}

Collection<LeftDto> leftList = new ArrayList<>();

Collection<RightDto> rightList = new ArrayList<>();

Может быть присоединен как,

Collection<JoinedDto> results = CQ.<LeftDto, LeftDto>query().from(leftList)
                .<RightDto, JoinedDto>innerJoin(CQ.<RightDto, RightDto>query().from(rightList))
                .on(LeftFyo::getId, RightDto::getLeftId)
                .transformDirect(selection ->  new JoinedDto(selection.getLeft().getText()
                                                     , selection.getLeft().getId()
                                                     , selection.getRight().getId())
                                 )
                .list();

Выражения

Filter<Dto> query = CQ.<Dto>filter()
    .from(testList)
    .where()
    .exec(s -> s.getId() + 1).eq().value(2);

Здесь есть действительно отличные ответы. Я хотел бы, чтобы все было как можно проще и читабельно:

public abstract class AbstractFilter<T> {

    /**
     * Method that returns whether an item is to be included or not.
     * @param item an item from the given collection.
     * @return true if this item is to be included in the collection, false in case it has to be removed.
     */
    protected abstract boolean excludeItem(T item);

    public void filter(Collection<T> collection) {
        if (CollectionUtils.isNotEmpty(collection)) {
            Iterator<T> iterator = collection.iterator();
            while (iterator.hasNext()) {
                if (excludeItem(iterator.next())) {
                    iterator.remove();
                }
            }
        }
    }
}

Просто реализуйте правильный excludeItem для каждого фильтра. В конечном итоге у вас будут отдельные фильтры, точно так же, как у вас есть сортировщики в коллекциях ...

Lawrence 07.01.2015 14:09

Мой ответ основан на этом от Кевина Вонга, здесь в качестве однострочного с использованием CollectionUtils из весна и выражения Java 8 лямбда.

CollectionUtils.filter(list, p -> ((Person) p).getAge() > 16);

Это так же лаконично и читабельно, как и любая альтернатива, которую я видел (без использования аспектно-ориентированных библиотек).

Spring CollectionUtils доступен с версии Spring 4.0.2.RELEASE, и помните, что вам нужен JDK 1.8 и уровень языка 8+.

Используя java 8, в частности lambda expression, вы можете сделать это просто, как в приведенном ниже примере:

myProducts.stream().filter(prod -> prod.price>10).collect(Collectors.toList())

где для каждого product в коллекции myProducts, если prod.price>10, добавить этот продукт в новый отфильтрованный список.

С гуавой:

Collection<Integer> collection = Lists.newArrayList(1, 2, 3, 4, 5);

Iterators.removeIf(collection.iterator(), new Predicate<Integer>() {
    @Override
    public boolean apply(Integer i) {
        return i % 2 == 0;
    }
});

System.out.println(collection); // Prints 1, 3, 5

Мне нужно было отфильтровать список в зависимости от значений, уже присутствующих в списке. Например, удалите все следующие значения, которые меньше текущего значения. {2 5 3 4 7 5} -> {2 5 7}. Или, например, чтобы удалить все дубликаты {3 5 4 2 3 5 6} -> {3 5 4 2 6}.

public class Filter {
    public static <T> void List(List<T> list, Chooser<T> chooser) {
        List<Integer> toBeRemoved = new ArrayList<>();
        leftloop:
        for (int right = 1; right < list.size(); ++right) {
            for (int left = 0; left < right; ++left) {
                if (toBeRemoved.contains(left)) {
                    continue;
                }
                Keep keep = chooser.choose(list.get(left), list.get(right));
                switch (keep) {
                    case LEFT:
                        toBeRemoved.add(right);
                        continue leftloop;
                    case RIGHT:
                        toBeRemoved.add(left);
                        break;
                    case NONE:
                        toBeRemoved.add(left);
                        toBeRemoved.add(right);
                        continue leftloop;
                }
            }
        }

        Collections.sort(toBeRemoved, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });

        for (int i : toBeRemoved) {
            if (i >= 0 && i < list.size()) {
                list.remove(i);
            }
        }
    }

    public static <T> void List(List<T> list, Keeper<T> keeper) {
        Iterator<T> iterator = list.iterator();
        while (iterator.hasNext()) {
            if (!keeper.keep(iterator.next())) {
                iterator.remove();
            }
        }
    }

    public interface Keeper<E> {
        boolean keep(E obj);
    }

    public interface Chooser<E> {
        Keep choose(E left, E right);
    }

    public enum Keep {
        LEFT, RIGHT, BOTH, NONE;
    }
}

Это будет использоваться вот так.

List<String> names = new ArrayList<>();
names.add("Anders");
names.add("Stefan");
names.add("Anders");
Filter.List(names, new Filter.Chooser<String>() {
    @Override
    public Filter.Keep choose(String left, String right) {
        return left.equals(right) ? Filter.Keep.LEFT : Filter.Keep.BOTH;
    }
});

Поскольку Java 9Collectors.filtering включен:

public static <T, A, R>
    Collector<T, ?, R> filtering(Predicate<? super T> predicate,
                                 Collector<? super T, A, R> downstream)

Таким образом, фильтрация должна быть:

collection.stream().collect(Collectors.filtering(predicate, collector))

Пример:

List<Integer> oddNumbers = List.of(1, 19, 15, 10, -10).stream()
            .collect(Collectors.filtering(i -> i % 2 == 1, Collectors.toList()));

В Java 8 вы можете напрямую использовать этот метод фильтрации, а затем сделать это.

 List<String> lines = Arrays.asList("java", "pramod", "example");

 List<String> result = lines.stream()              
         .filter(line -> !"pramod".equals(line))     
         .collect(Collectors.toList());              

 result.forEach(System.out::println); 

Другие вопросы по теме