Возможно, вы захотите использовать filter()
в качестве встроенного.
Подробнее проверьте здесь
Java 8 ( 2014 ) решает эту проблему, используя потоки и lambdas в одной строке кода:
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
С помощью функции 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();
. Учитывая коллекцию [The, quick, brown, fox, jump, over, the, lazy, dog], это приводит к [быстрому, коричневому , jumps, over, lazy], то есть все строки длиной более трех символов.
Все стили итераций, поддерживаемые DSL ForEach, это
AllSatisfy
AnySatisfy
Collect
Counnt
CutPieces
Detect
GroupedBy
IndexOf
InjectInto
Reject
Select
Для получения дополнительной информации см. https://www.iam.unibe.ch/scg/svn_repos/Sources/ForEach
Предполагая, что вы используете 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);
Если производительность на линейной проверке вызывает беспокойство, тогда я могу захотеть иметь объект домена который имеет целевую коллекцию. Объект домена, у которого есть целевая коллекция, будет иметь логику фильтрации для методов, которые инициализируют, добавляют и устанавливают целевую коллекцию.
UPDATE:
В классе утилиты (скажем, 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));
UPDATE (после выпуска Java 8):
Прошло несколько лет с тех пор, как я (Алан) впервые опубликовал этот ответ, и я до сих пор не могу поверить, что собираю SO-очки для этого ответ. Во всяком случае, теперь, когда 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();
API JDK 8 для опциональных опций имеет возможность 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.
Простое решение до 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
Более подробно о RxJava filter
можно найти здесь здесь .
«Лучший» способ слишком широк. Это «кратчайший»? «Самый быстрый»? "Удобочитаемый"? Фильтр на месте или в другую коллекцию?
Простейший (но не самый читаемый) способ заключается в его повторении и использовании метода 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 (), чтобы узнать, сохраняется ли экземпляр в сборнике.
Я действительно не вижу оправдания для привлечения сторонней библиотеки только для этой задачи.
Используйте Engine Query Engine (CQEngine) . Это, безусловно, самый быстрый способ сделать это.
См. Также: Как вы запрашиваете коллекции объектов в Java (Критерии / SQL-подобные)?
Давайте посмотрим, как фильтровать встроенный список JDK и MutableList с использованием коллекций Eclipse (ранее коллекции GS ).
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 Collections 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.
Так как 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()));
Мне нужно было отфильтровать список в зависимости от значений, уже присутствующих в списке. Например, удалите все значения, следующие за меньшим, чем текущее значение. {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 8
, в частности lambda expression
, вы можете сделать это просто как приведенный ниже пример:
myProducts.stream().filter(prod -> prod.price>10).collect(Collectors.toList())
, где для каждой коллекции product
внутри myProducts
, если prod.price>10
, затем добавьте этот продукт в новый отфильтрованный список.
Метод Collections2.filter (Collection, Predicate) в в библиотеке Гуавы Google делает именно то, что вы ищете.
Это, в сочетании с отсутствием реальных закрытий, является моей самой большой проблемой для 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");
Настройка:
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();
}
});
С раннего выпуска 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);
JFilter http://code.google.com/p/jfilter/ лучше всего подходит для вашего требования.
JFilter - это простая и высокопроизводительная библиотека с открытым исходным кодом для набор запросов к Java-компонентам.
Основные функции
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);
Как насчет какой-то простой и скрытой 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 () статически импортируется
Используйте CollectionUtils.filter (Collection, Predicate) , из Apache Commons.
Ожидание 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());
Вы действительно хотите отфильтровать саму коллекцию, а не итератор?
см. org.apache.commons.collections.iterators.FilterIterator
или с использованием версии 4 apache commons org.apache.commons.collections4.iterators.FilterIterator
Рассмотрим Коллекции Google для обновленной структуры коллекций, которая поддерживает общие файлы.
UPDATE: библиотека коллекций google теперь устарела. Вместо этого вы должны использовать последнюю версию Guava . Он все еще имеет все те же расширения к структуре коллекций, включая механизм фильтрации на основе предиката.
Некоторые действительно большие отличные ответы здесь. Я, я бы хотел, чтобы они были максимально простыми и читаемыми:
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();
}
}
}
}
}
Мой ответ основывается на том, что от Кевина Вонга, здесь как однострочный, используя CollectionUtils
из spring и выражения Java 8 лямбда .
CollectionUtils.filter(list, p -> ((Person) p).getAge() > 16);
Это краткое и читаемое как любая альтернатива, которую я видел (без использования аспектно-ориентированных библиотек)
Spring CollectionUtils доступен с весны 4.0.2. RELEASE, и помните, что вам нужен JDK 1.8 и уровень языка 8 +.
Я написал расширенный класс 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);
}
}
С Guava:
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