Каков наилучший способ фильтрации коллекции Java? - PullRequest
627 голосов
/ 23 сентября 2008

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

Ответы [ 27 ]

7 голосов
/ 23 сентября 2008

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

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

или с использованием версии 4 общих Apache org.apache.commons.collections4.iterators.FilterIterator

6 голосов
/ 25 сентября 2012

Давайте посмотрим, как фильтровать встроенный список JDK и MutableList , используя Eclipse Collections (ранее GS Collections ).

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);

Вот как вы можете фильтровать, используя анонимный внутренний класс как 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 с использованием фабрики Predicates .

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

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

Вот версия, которая не выделяет объект для предиката, используя вместо этого фабрику Predicates2 с методом 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));

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

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

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

Метод 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 Collections.

5 голосов
/ 03 декабря 2008

С 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();

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

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

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

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

5 голосов
/ 23 сентября 2008

Метод Collections2.filter (Collection, Predicate) в Библиотека Google Guava делает именно то, что вы ищете.

4 голосов
/ 24 мая 2018

Так как java 9 Collectors.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()));
3 голосов
/ 21 января 2010

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

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

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

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

OR

LinkedList<Person> list = ....
LinkedList<Person> filtered = Query.from(list).where("x => x.age >= 21");
2 голосов
/ 07 января 2015

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

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();
                }
            }
        }
    }
}
2 голосов
/ 05 апреля 2010

Я написал расширенный класс 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);
    }
}
2 голосов
/ 05 апреля 2012

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

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

Основные характеристики

  • Поддержка свойств коллекции (java.util.Collection, java.util.Map и Array).
  • Поддержка сбора внутри коллекции любой глубины.
  • Поддержка внутренних запросов.
  • Поддержка параметризованных запросов.
  • Может отфильтровать 1 миллион записей за 100 мс.
  • Фильтр (запрос) дается в простом формате json, он похож на запросы Мангодба. Ниже приведены некоторые примеры.
  • {"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 или цена sku в 20 и 40 и код sku «RedApple».
2 голосов
/ 30 июля 2012

Использование Сборщик запросов (CQEngine) . Это самый быстрый способ сделать это.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...