Каков наилучший способ фильтрации коллекции Java?

Ответы:

699

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

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

Вот учебник .

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

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

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

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

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

Правовая оговорка: я - участник на lambdaj

Марио Фуско
источник
34
Хорошо, но статический импорт запутывает происходящее. Для справки, выберите / имеющие / включены статический импорт в ch.lambdaj.Lambda, больше - org.hamcrest.Matchers
MikePatel
11
LambdaJ действительно сексуален, но стоит отметить, что он подразумевает значительные накладные расходы (в среднем 2,6): code.google.com/p/lambdaj/wiki/PerformanceAnalysis .
Док Давлуз
7
Очевидно, не работает на Android: groups.google.com/forum/#!msg/lambdaj/km7uFgvSd3k/grJhgl3ik5sJ
Мориц
7
Очень нравится этот пример LamdaJ ... похож на встроенные функции .NET лямбда .NET. А где человек может пить в 16 лет? Мы должны рассмотреть возможность добавления ограничения локализации. : P
MAbraham1
3
removeIf пример должен бытьpersons.removeIf(p -> p.getAge() <= 16);
vim
224

Предполагая, что вы используете Java 1.5 , и что вы не можете добавить Google Collections , я бы сделал нечто очень похожее на то, что сделали ребята из 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) я добавил метод select с опцией для значения по умолчанию, когда предикат не возвращает ожидаемое значение, а также статическое свойство для параметров, которые будут использоваться внутри нового 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.

Алан
источник
27
Да, но я ненавижу изобретать велосипед, снова и снова. Я предпочел бы найти какую-нибудь служебную библиотеку, которая делает, когда я хочу.
Кевин Вонг
2
Это не лучший способ, если вы не хотите новую коллекцию. Используйте метафору итератора фильтра, которая может вводить в новую коллекцию, или это может быть все, что вам нужно.
Джош
@Nestor: в понимании Scala фильтрация была бы намного проще:val authorized = for (user <- users if user.isAuthorized) yield user
Алан
Изменяет ли это оригинальную коллекцию или создает новую? Я попытался использовать этот метод и зарегистрировал обе мои коллекции (оригинал и ту, что возвращена из метода), они одинаковы. @ Алан
Рохан
1
@Rohan, это не предназначено, чтобы изменить оригинальную коллекцию. Обратите внимание, что вышеупомянутая коллекция результатов создается заново, а метод фильтра добавляет коллекцию результатов только в том случае, если применяется предикат.
Алан
92

Используйте CollectionUtils.filter (Collection, Predicate) от Apache Commons.

Кевин Вонг
источник
3
это нормально, но это не универсально, и изменяет коллекцию на месте (не приятно)
Кевин Вонг
2
В CollectionUtils есть другие методы фильтрации, которые не изменяют исходную коллекцию.
Скаффман
42
В частности, метод, который не изменяет коллекцию на месте, это org.apache.commons.collections.CollectionUtils # select (Collection, Predicate)
Eero,
5
В Commons Collections v4 теперь используется Generics.
Джастин Эмери
1
Этот метод следует использовать с осторожностью, так как он опирается (по крайней мере, на реализацию commons-collection-3.2.1) на метод iterator.remove (), который является необязательным для коллекций, поэтому вместо фильтрации, скажем, массива вы можете получить исключение UnsupportedOperationException.
user2417480 15.10.15
67

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

Простейший (но не самый читаемый) способ - выполнить итерацию и использовать метод 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 (), чтобы узнать, будет ли экземпляр храниться в коллекции.

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

Владимир Дюжев
источник
6
Мой голос идет за это: он просто работает, без внешних библиотек. Я никогда не думал, что создание Итератора может быть полезным по сравнению с использованием синтаксиса for-each или тем, что вы можете удалять элементы из списка без исключения ConcurrentModificationException или чего-то в этом роде. :)
ZeroOne
1
Я думаю, что это лучший способ использовать стандартную библиотеку Java без копирования. Для 1.8 была бы stream()особенность, но не каждый может поиграть с новейшими игрушками: P
Populus
Это тоже изменяет оригинальную коллекцию? @ ZeroOne
Рохан
Да, конечно, @Rohan. Попробуйте, если не верите. ;)
ZeroOne
Хаха, я сделал! Но я хочу сохранить свою оригинальную коллекцию. Можете ли вы предложить способ сделать это без добавления внешней библиотеки? @ ZeroOne
Рохан
62

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

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

Хит Границы
источник
да, я знал о библиотеках Google. В версии, которую я использовал, не было Collections2. Я добавил новый ответ на этот вопрос, в котором перечислены конкретные методы.
Кевин Вонг
7
Кевин, Iterables.filter () и Iterators.filter () были там с самого начала, и, как правило, все, что вам нужно.
Кевин Бурриллион
28

Ждите 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());
gavenkoa
источник
13
Тьфу ... это так многословно. Почему они не могли просто сделать: List <Person> result = personList.filter (p -> p.age> 30);
Кевин Вонг
8
Чтобы использовать фильтр непосредственно в коллекции, вам нужно использовать команду removeIf : download.java.net/jdk8/docs/api/java/util/…
gavenkoa
6
@KevinWong "многословный" в значительной степени описывает весь язык, который я думаю. По крайней мере, они последовательны?
Изгой
5
Почему бы не использовать Collectors.toList () в последней части?
Нестор Эрнандес Лоли
3
Вот ссылка gavenkoa при условии, что не 404. personList.removeIf(p -> p.age < 30);Меньше многословно. Кроме того, я слышал разговоры о том, чтобы начать реализовывать apis, которые принимают и возвращают Streams, а не Collections, потому что Streams очень полезны и быстры, но идти к ним и из них идет медленно.
Капитан Мэн
11

Начиная с раннего выпуска 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);
Джош М
источник
11

Я добавлю 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

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

Брайан Боуман
источник
7

Настройка:

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();
  }
});
джон
источник
2
Хорошо, но я предпочитаю реализацию Alan, потому что вы получаете копию коллекции вместо ее изменения. Более того, код Алана является потокобезопасным, а ваш - нет.
Marcospereira
7

Как насчет простой и понятной 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 () статически импортируется

Нестор Эрнандес Лоли
источник
7

Давайте посмотрим, как фильтровать встроенный список 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 и MutableList коллекций Eclipse с использованием фабрики 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 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 Collections.

Дональд Рааб
источник
1
Как бы вы сделали в removeIfсписке или установить для примитивов?
Вивек Рао
API для removeIf был добавлен к примитивным коллекциям в EC 9.1. eclipse.org/collections/javadoc/9.1.0/org/eclipse/collections/…
Дональд Рааб
5

С 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

akuhn
источник
Это довольно умно! Хотя много работы по реализации хорошего синтаксиса Ruby-ish! Отрицательным является то, что ваш фильтр не является первоклассной функцией и, следовательно, не может быть использован повторно.
Бросай
Хорошая точка зрения. Один из способов повторного использования тела цикла - это рефакторинг цикла в метод, который принимает запрос выбора в качестве параметра. Это, однако, далеко не так удобно и мощно, как настоящие замыкания.
akuhn
5

Поскольку 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()));
yanefedor
источник
3

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

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

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

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");
jdc0589
источник
Ссылка на сайт? Даже если ваша библиотека неэффективна или непригодна для других целей, было бы интересно посмотреть, доступен ли источник.
MatrixFrog
Сделал репо публичным ( net-machine.com/indefero/p/jdclib/source/tree/master ). Вы заинтересованы в выражении пакета. В тестовом пакете есть тестер с примером использования. Я никогда особо не работал над интерфейсом строковых запросов, на который ссылались выше (не хотелось писать настоящий парсер), так что явный интерфейс запросов в тестере - это путь.
jdc0589
2

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.
  • {'$ или': [{'code': '10'}, {'skus': {'$ and': [{'price': {'$ in': ['20', '40']} }, {'code': 'RedApple'}]}}]}
    • Выберите все продукты, для которых код продукта 10 или цена sku в 20 и 40 и код sku «RedApple».
Кямран Али Хан
источник
1
Вы должны отрицать, что вы являетесь автором (как я думаю, это так).
assylias
Да, я являюсь автором этой библиотеки.
Камран Али Хан
2

Я написал расширенный класс 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

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

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 для каждого фильтра. В итоге у вас будут отдельные фильтры точно так же, как у вас есть сортировщики в коллекциях ...
Лоуренс
1

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

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

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

Эндрю Макнайт
источник
1

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);
Низко летящий пеликан
источник
1

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

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

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

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

vikingsteve
источник
1

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

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

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

hd84335
источник
1

Мне нужно было отфильтровать список в зависимости от значений, уже присутствующих в списке. Например, удалите все значения, следующие ниже, чем текущее значение. {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;
    }
});
Фредрик Меткалф
источник
0

С гуавой:

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
ZhekaKozlov
источник
0

В 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); 
pramod_m
источник