Я хочу відфільтрувати на java.util.Collection
основі присудка.
Я хочу відфільтрувати на java.util.Collection
основі присудка.
Відповіді:
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)));
Ви можете уявити щось більш читабельне?
Відмова: Я є учасником ламбдай
persons.removeIf(p -> p.getAge() <= 16);
Якщо припустити, що ви використовуєте 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);
Якщо виконання лінійної перевірки викликає занепокоєння, то я, можливо, захочу мати об’єкт домену, який має цільову колекцію. Об'єкт домену, який має цільову колекцію, повинен мати логіку фільтрації для методів, які ініціалізують, додають та встановлюють цільовий збірник.
ОНОВЛЕННЯ:
У класі утиліти (скажімо, предикат) я додав метод вибору з опцією значення за замовчуванням, коли предикат не повертає очікуваного значення, а також статичну властивість для парамм, які будуть використовуватися всередині нового 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.
val authorized = for (user <- users if user.isAuthorized) yield user
Використовуйте CollectionUtils.filter (Колекція, предикат) від Apache Commons.
"Найкращий" спосіб - це занадто широкий запит. Це "найкоротше"? "Найшвидший"? "Читабельний"? Фільтр на місці чи в іншій колекції?
Найпростіший (але не найбільш читаний) спосіб - це повторити його та використовувати метод 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 (), щоб дізнатися, чи повинен зберігатися екземпляр у колекції.
Я дійсно не бачу виправдання для залучення сторонніх бібліотек саме для цього завдання.
stream()
функцією, але не всі отримують можливість грати з найновішими іграшками: P
Розгляньте колекції Google для оновленої структури колекцій, яка підтримує дженерики.
ОНОВЛЕННЯ : Бібліотека колекцій Google зараз застаріла. Слід скористатись останньою версією Guava . Він досі має все ті ж розширення до системи колекцій, включаючи механізм фільтрації на основі присудка.
Зачекайте на 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());
personList.removeIf(p -> p.age < 30);
Менше багатослівного. Крім того, я чув розмови про початок впровадження apis, які приймають і повертають Stream
s, а не Collection
s, тому що Stream
s дуже корисні і швидкі, але перехід до / з них повільний.
З самого раннього випуску 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);
Я кину 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
можна прочитати тут .
Установка:
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
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 () статично імпортується
Ви впевнені, що хочете відфільтрувати саму колекцію, а не ітератор?
див. org.apache.commons.collections.iterators.FilterIterator
або за допомогою версії 4 апаш-комменс org.apache.commons.collections4.iterators.FilterIterator
Давайте розглянемо, як фільтрувати вбудований список JDK та MutableList за допомогою Eclipse 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);
Ось як можна фільтрувати, використовуючи лямбда 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 Collections MutableLists за допомогою фабрики Predicate .
Assert.assertEquals(selected, Iterate.select(jdkList, Predicates.lessThan(3)));
Assert.assertEquals(selected, ecList.select(Predicates.lessThan(3)));
Ось версія, яка не виділяє об’єкт для предиката, використовуючи замість цього заводу метод Predicate2, а не selectWith
метод, який використовує a 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.
removeIf
список у списку чи наборі для примітивів?
З DSL ForEach ви можете писати
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();
Враховуючи колекцію [Швидкий, коричневий, лисиця, стрибки, над, ледачий, собака], це призводить до [швидкого, коричневого, стрибків, лінивого], тобто всіх рядків довше трьох символів.
Усі стилі ітерації, що підтримуються DSL ForEach, є
AllSatisfy
AnySatisfy
Collect
Counnt
CutPieces
Detect
GroupedBy
IndexOf
InjectInto
Reject
Select
Для отримання детальної інформації див. Https://www.iam.unibe.ch/scg/svn_repos/Sources/ForEach
Метод Collections2.filter (Колекція, присудок) в бібліотеці Google Guava Google робить саме те, що ви шукаєте.
Оскільки 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()));
Це в поєднанні з відсутністю реальних закриттів є моїм найбільшим захопленням для 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");
JFilter http://code.google.com/p/jfilter/ найкраще відповідає вашим вимогам.
JFilter - це проста і високопродуктивна бібліотека з відкритим кодом для запиту колекції Java-бобів.
Ключові особливості
Я написав розширений клас 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);
}
}
Використовуйте механізм колекційних запитів (CQEngine) . Це, безумовно, найшвидший спосіб зробити це.
Дивіться також: Як ви шукаєте колекції об’єктів на Java (Критерії / SQL-подібні)?
Ось тут справді чудові чудові відповіді. Мені хотілося б, щоб тонкі були максимально простими і читалими:
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();
}
}
}
}
}
Просте рішення перед Java8:
ArrayList<Item> filtered = new ArrayList<Item>();
for (Item item : items) if (condition(item)) filtered.add(item);
На жаль, це рішення не є повністю загальним, виводить список, а не тип даної колекції. Крім того, залучення до бібліотек або функцій запису, які обробляють цей код, здається мені надмірним, якщо умова не є складною, але тоді ви можете написати функцію для умови.
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);
Моя відповідь ґрунтується на тому , що від Кевіна Вонга, ось як однострочнікі з використанням CollectionUtils
від пружини і Java 8 лямбда - вираження.
CollectionUtils.filter(list, p -> ((Person) p).getAge() > 16);
Це настільки стисло і читабельно, як і будь-яка альтернатива, яку я бачив (без використання бібліотек, що базуються на аспектах)
Spring CollectionUtils доступний з весняної версії 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
, тоді додайте цей продукт до нового відфільтрованого списку.
Мені потрібно було фільтрувати список залежно від значень, вже наявних у списку. Наприклад, видаліть усі наступні значення, менші за поточне значення. {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;
}
});
З Гуавою:
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
У 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);