Chcę filtrować na java.util.Collection
podstawie predykatu.
Chcę filtrować na java.util.Collection
podstawie predykatu.
Odpowiedzi:
Java 8 ( 2014 ) rozwiązuje ten problem za pomocą strumieni i lambd w jednym wierszu kodu:
List<Person> beerDrinkers = persons.stream()
.filter(p -> p.getAge() > 16).collect(Collectors.toList());
Oto samouczek .
Służy Collection#removeIf
do modyfikowania kolekcji na miejscu. (Uwaga: w tym przypadku predykat usunie obiekty, które spełniają predykat):
persons.removeIf(p -> p.getAge() <= 16);
lambdaj pozwala filtrować kolekcje bez pisania pętli i wewnętrznych klas:
List<Person> beerDrinkers = select(persons, having(on(Person.class).getAge(),
greaterThan(16)));
Czy możesz sobie wyobrazić coś bardziej czytelnego?
Oświadczenie: Jestem współtwórcą lambdaj
persons.removeIf(p -> p.getAge() <= 16);
Zakładając, że używasz Java 1.5 i nie możesz dodawać kolekcji Google , zrobiłbym coś bardzo podobnego do tego, co zrobili chłopaki Google. To niewielka zmiana w komentarzach Jona.
Najpierw dodaj ten interfejs do swojej bazy kodów.
public interface IPredicate<T> { boolean apply(T type); }
Jego realizatorzy mogą odpowiedzieć, gdy określony predykat jest prawdziwy określonego typu. Np. Jeśli T
były User
i AuthorizedUserPredicate<User>
implementuje IPredicate<T>
, to AuthorizedUserPredicate#apply
zwraca, czy przekazaneUser
jest autoryzowany.
W niektórych klasach użytkowych można powiedzieć
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;
}
Zakładając, że masz do dyspozycji powyższe, może być
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);
Jeśli problem dotyczy wydajności kontroli liniowej, może chciałbym mieć obiekt domeny z kolekcją docelową. Obiekt domeny, który ma kolekcję docelową, miałby logikę filtrowania dla metod inicjujących, dodających i ustawiających kolekcję docelową.
AKTUALIZACJA:
W klasie użytkowej (powiedzmy Predicate) dodałem metodę select z opcją wartości domyślnej, gdy predykat nie zwraca oczekiwanej wartości, a także właściwość statyczną dla parametrów, które będą używane w nowym 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;
}
}
Poniższy przykład szuka brakujących obiektów między kolekcjami:
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;
}
});
Poniższy przykład wyszukuje instancję w kolekcji i zwraca pierwszy element kolekcji jako wartość domyślną, gdy instancja nie zostanie znaleziona:
MyType myObject = Predicate.select(collectionOfMyType, new IPredicate<MyType>() {
public boolean apply(MyType objectOfMyType) {
return objectOfMyType.isDefault();
}}, collectionOfMyType.get(0));
AKTUALIZACJA (po wydaniu Java 8):
Minęło kilka lat, odkąd (Alan) po raz pierwszy opublikowałem tę odpowiedź, i nadal nie mogę uwierzyć, że zbieram punkty SO za tę odpowiedź. W każdym razie, teraz, gdy Java 8 wprowadziła zamknięcia języka, moja odpowiedź byłaby teraz znacznie inna i prostsza. W Javie 8 nie ma potrzeby posiadania odrębnej statycznej klasy narzędzi. Więc jeśli chcesz znaleźć pierwszy element, który pasuje do twojego predykatu.
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 dla optionals ma możliwość get()
, isPresent()
, orElse(defaultUser)
, orElseGet(userSupplier)
i orElseThrow(exceptionSupplier)
, a także inne funkcje „” monadycznego takie jak map
, flatMap
ifilter
.
Jeśli chcesz po prostu zebrać wszystkich użytkowników, którzy pasują do predykatu, użyj przycisku, Collectors
aby zakończyć strumień w żądanej kolekcji.
final UserService userService = ... // perhaps injected IoC
final List<UserModel> userOption = userCollection.stream().filter(u -> {
boolean isAuthorized = userService.isAuthorized(u);
return isAuthorized;
}).collect(Collectors.toList());
Zobacz tutaj po więcej przykładów, w jaki sposób Java 8 strumieni pracy.
val authorized = for (user <- users if user.isAuthorized) yield user
Użyj CollectionUtils.filter (Collection, Predicate) , z Apache Commons.
„Najlepszy” sposób to zbyt szerokie żądanie. Czy to jest „najkrótsze”? "Najszybszy"? "Czytelny"? Filtrować w miejscu lub w innej kolekcji?
Najprostszym (ale niezbyt czytelnym) sposobem jest iteracja i użycie metody Iterator.remove ():
Iterator<Foo> it = col.iterator();
while( it.hasNext() ) {
Foo foo = it.next();
if( !condition(foo) ) it.remove();
}
Teraz, aby uczynić go bardziej czytelnym, możesz zawinąć go w metodę narzędzia. Następnie wymyśl interfejs IPredicate, utwórz anonimową implementację tego interfejsu i zrób coś takiego:
CollectionUtils.filterInPlace(col,
new IPredicate<Foo>(){
public boolean keepIt(Foo foo) {
return foo.isBar();
}
});
gdzie filterInPlace () iteruje kolekcję i wywołuje Predicate.keepIt (), aby dowiedzieć się, czy instancja ma być przechowywana w kolekcji.
Naprawdę nie widzę uzasadnienia dla wprowadzenia biblioteki innej firmy tylko do tego zadania.
stream()
funkcja, ale nie wszyscy mogą bawić się najnowszymi zabawkami: P
Zastanów się nad kolekcjami Google, aby uzyskać zaktualizowaną strukturę kolekcji, która obsługuje generyczne.
AKTUALIZACJA : Biblioteka kolekcji Google jest teraz przestarzała. Zamiast tego powinieneś użyć najnowszej wersji Guava . Nadal ma te same rozszerzenia w strukturze kolekcji, w tym mechanizm filtrowania oparty na predykacie.
Poczekaj na Javę 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);
Mniej gadatliwy. Słyszałem też o tym, jak zacząć wdrażać api, które akceptują i zwracają Stream
s zamiast Collection
s, ponieważ Stream
są bardzo przydatne i szybkie, ale przechodzenie do / z nich jest powolne.
Od wczesnej wersji Java 8 możesz wypróbować coś takiego:
Collection<T> collection = ...;
Stream<T> stream = collection.stream().filter(...);
Na przykład, jeśli masz listę liczb całkowitych i chcesz przefiltrować liczby o wartości> 10, a następnie wydrukować te liczby na konsoli, możesz zrobić coś takiego:
List<Integer> numbers = Arrays.asList(12, 74, 5, 8, 16);
numbers.stream().filter(n -> n > 10).forEach(System.out::println);
Dorzucę RxJava w pierścieniu, który jest również dostępny na Androida . RxJava nie zawsze może być najlepszą opcją, ale da ci większą elastyczność, jeśli chcesz dodać więcej transformacji w swojej kolekcji lub obsłużyć błędy podczas filtrowania.
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);
}
});
Wynik:
1
3
5
Więcej informacji na temat RxJava filter
można znaleźć tutaj .
Ustawić:
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();
}
}
}
Użycie:
List<MyObject> myList = ...;
filterCollection(myList, new Predicate<MyObject>() {
public boolean filter(MyObject obj) {
return obj.shouldFilter();
}
});
Co powiesz na prostą i prostą Javę
List<Customer> list ...;
List<Customer> newList = new ArrayList<>();
for (Customer c : list){
if (c.getName().equals("dd")) newList.add(c);
}
Prosty, czytelny i łatwy (i działa na Androidzie!) Ale jeśli używasz Java 8, możesz to zrobić w jednym słodkim wierszu:
List<Customer> newList = list.stream().filter(c -> c.getName().equals("dd")).collect(toList());
Zauważ, że toList () jest importowany statycznie
Czy na pewno chcesz filtrować samą kolekcję, a nie iterator?
zobacz org.apache.commons.collections.iterators.FilterIterator
lub używając wersji 4 apache commons org.apache.commons.collections4.iterators.FilterIterator
Przyjrzyjmy się, jak filtrować wbudowaną listę JDK i MutableList przy użyciu kolekcji Eclipse .
List<Integer> jdkList = Arrays.asList(1, 2, 3, 4, 5);
MutableList<Integer> ecList = Lists.mutable.with(1, 2, 3, 4, 5);
Jeśli chcesz filtrować liczby mniejsze niż 3, możesz oczekiwać następujących wyników.
List<Integer> selected = Lists.mutable.with(1, 2);
List<Integer> rejected = Lists.mutable.with(3, 4, 5);
Oto, w jaki sposób można filtrować, używając lambda Java 8 jako 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));
Oto, w jaki sposób możesz filtrować za pomocą anonimowej klasy wewnętrznej jako 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));
Oto kilka alternatyw dla filtrowania list JDK i MutableLists kolekcji Eclipse Kolekcje przy użyciu fabryki predykatów .
Assert.assertEquals(selected, Iterate.select(jdkList, Predicates.lessThan(3)));
Assert.assertEquals(selected, ecList.select(Predicates.lessThan(3)));
Oto wersja, która nie przydziela obiektu do predykatu, używając zamiast tego fabryki Predicates2 z selectWith
metodą, która wymaga Predicate2
.
Assert.assertEquals(
selected, ecList.selectWith(Predicates2.<Integer>lessThan(), 3));
Czasami chcesz filtrować według stanu ujemnego. W tym celu istnieje specjalna metoda w kolekcji Eclipse reject
.
Assert.assertEquals(rejected, Iterate.reject(jdkList, lessThan3));
Assert.assertEquals(rejected, ecList.reject(lessThan3));
Metoda partition
zwróci dwie kolekcje, zawierające elementy wybrane przez i odrzucone przez 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());
Uwaga: jestem osobą odpowiedzialną za kolekcje Eclipse.
removeIf
na liście lub zestawie dla prymitywów?
Z ForEach DSL możesz pisać
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();
Biorąc pod uwagę zbiór [The, quick, brown, lis, jumps, over, the, leniwy, pies] skutkuje to [szybkim, brązowym, jumps, over, leniwym], tj. Wszystkie łańcuchy dłuższe niż trzy znaki.
Wszystkie style iteracji obsługiwane przez ForEach DSL są
AllSatisfy
AnySatisfy
Collect
Counnt
CutPieces
Detect
GroupedBy
IndexOf
InjectInto
Reject
Select
Aby uzyskać więcej informacji, zobacz https://www.iam.unibe.ch/scg/svn_repos/Sources/ForEach
Metoda Collections2.filter (Collection, Predicate) w bibliotece Google Guava robi dokładnie to, czego szukasz.
Ponieważ Java 9 Collectors.filtering
jest włączona:
public static <T, A, R>
Collector<T, ?, R> filtering(Predicate<? super T> predicate,
Collector<? super T, A, R> downstream)
Dlatego filtrowanie powinno być:
collection.stream().collect(Collectors.filtering(predicate, collector))
Przykład:
List<Integer> oddNumbers = List.of(1, 19, 15, 10, -10).stream()
.collect(Collectors.filtering(i -> i % 2 == 1, Collectors.toList()));
To, w połączeniu z brakiem prawdziwych zamknięć, jest moim największym problemem dla Javy. Szczerze mówiąc, większość wyżej wymienionych metod jest dość łatwa do odczytania i NAPRAWDĘ wydajna; jednak po spędzeniu czasu z .Net, Erlang itp. zintegrowane na poziomie języka zrozumienie listy sprawia, że wszystko jest o wiele czystsze. Bez dodatków na poziomie językowym Java nie może być tak czysta, jak wiele innych języków w tym obszarze.
Jeśli wydajność jest ogromnym problemem, kolekcje Google to droga (lub napisz własne proste narzędzie predykcyjne). Składnia Lambdaj jest bardziej czytelna dla niektórych osób, ale nie jest tak wydajna.
A potem jest biblioteka, którą napisałem. Zignoruję wszelkie pytania dotyczące jego skuteczności (tak, to takie złe) ...... Tak, wiem, że jest oparte na wyraźnej refleksji i nie, nie używam go, ale działa:
LinkedList<Person> list = ......
LinkedList<Person> filtered =
Query.from(list).where(Condition.ensure("age", Op.GTE, 21));
LUB
LinkedList<Person> list = ....
LinkedList<Person> filtered = Query.from(list).where("x => x.age >= 21");
JFilter http://code.google.com/p/jfilter/ najlepiej pasuje do twoich wymagań.
JFilter to prosta i wysokowydajna biblioteka open source do przeszukiwania kolekcji ziaren Java.
Kluczowe cechy
Napisałem rozszerzoną klasę Iterable, która obsługuje stosowanie algorytmów funkcjonalnych bez kopiowania zawartości kolekcji.
Stosowanie:
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);
}
Powyższy kod zostanie faktycznie wykonany
for( int n : myList )
{
if( n % 2 == 0 )
{
System.out.println(n);
}
}
Użyj Collection Query Engine (CQEngine) . To zdecydowanie najszybszy sposób na zrobienie tego.
Zobacz także: Jak przeszukiwać kolekcje obiektów w Javie (podobne do kryteriów / SQL)?
Oto kilka naprawdę świetnych odpowiedzi. Ja chciałbym, aby cienkie były tak proste i czytelne, jak to możliwe:
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();
}
}
}
}
}
Proste rozwiązanie wcześniejsze niż Java8:
ArrayList<Item> filtered = new ArrayList<Item>();
for (Item item : items) if (condition(item)) filtered.add(item);
Niestety to rozwiązanie nie jest w pełni ogólne, generując listę, a nie typ danej kolekcji. Także wprowadzanie bibliotek lub pisanie funkcji, które zawijają ten kod, wydaje mi się przesadą, chyba że warunek jest złożony, ale wtedy możesz napisać funkcję dla tego warunku.
https://code.google.com/p/joquery/
Obsługuje różne możliwości,
Biorąc pod uwagę kolekcję,
Collection<Dto> testList = new ArrayList<>();
typu
class Dto
{
private int id;
private String text;
public int getId()
{
return id;
}
public int getText()
{
return text;
}
}
Filtr
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();
Również,
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"});
Sortowanie (dostępne również dla Java 7)
Filter<Dto> query = CQ.<Dto>filter(testList)
.orderBy()
.property(Dto::getId)
.property(Dto::getName)
Collection<Dto> sorted = query.list();
Grupowanie (dostępne również dla Java 7)
GroupQuery<Integer,Dto> query = CQ.<Dto,Dto>query(testList)
.group()
.groupBy(Dto::getId)
Collection<Grouping<Integer,Dto>> grouped = query.list();
Połączenia (dostępne również dla Java 7)
Dany,
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<>();
Można dołączyć,
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();
Wyrażenia
Filter<Dto> query = CQ.<Dto>filter()
.from(testList)
.where()
.exec(s -> s.getId() + 1).eq().value(2);
Moja odpowiedź opiera się na tej odpowiedzi Kevina Wonga, tutaj jako jednowierszowej, wykorzystującej CollectionUtils
od wiosny i wyrażenia lambda Java 8 .
CollectionUtils.filter(list, p -> ((Person) p).getAge() > 16);
Jest to tak zwięzłe i czytelne, jak każda alternatywa, którą widziałem (bez korzystania z bibliotek aspektowych)
Spring CollectionUtils jest dostępny od wersji wiosennej 4.0.2. ZWOLNIJ, i pamiętaj, że potrzebujesz JDK 1.8 i poziomu językowego 8+.
Używając java 8
, lambda expression
możesz zrobić to po prostu jak w poniższym przykładzie:
myProducts.stream().filter(prod -> prod.price>10).collect(Collectors.toList())
gdzie dla każdej kolekcji product
wewnętrznej myProducts
, jeśli prod.price>10
, to dodaj ten produkt do nowej filtrowanej listy.
Musiałem przefiltrować listę w zależności od wartości już obecnych na liście. Na przykład usuń wszystkie następujące wartości, które są mniejsze niż bieżąca wartość. {2 5 3 4 7 5} -> {2 5 7}. Lub na przykład, aby usunąć wszystkie duplikaty {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;
}
}
To będzie używane w ten sposób.
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;
}
});
Z 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
W Javie 8 możesz bezpośrednio użyć tej metody filtrowania, a następnie to zrobić.
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);