Jaki jest najlepszy sposób na filtrowanie kolekcji Java?


Odpowiedzi:


699

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#removeIfdo 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


34
Ładnie, ale statyczny import zaciemnia to, co się dzieje. Dla porównania, zaznacz / posiadaj / wł. To statyczny import na ch.lambdaj.Lambda, greaterThan jest org.hamcrest.Matchers
MikePatel

11
LambdaJ jest naprawdę seksowny, ale warto zauważyć, że pociąga to za sobą znaczne koszty ogólne (średnio 2,6): code.google.com/p/lambdaj/wiki/PerformanceAnalysis .
Doc Davluz,

7
Najwyraźniej nie działa na Androidzie: groups.google.com/forum/#!msg/lambdaj/km7uFgvSd3k/grJhgl3ik5sJ
Moritz

7
Naprawdę podoba się ten przykład LamdaJ ... podobny do wbudowanych funkcji Lambda .NET. A gdzie można pić w wieku 16 lat? Powinniśmy rozważyć dodanie ograniczenia lokalizacji. : P
MAbraham1,

3
removeIf przykładem powinien byćpersons.removeIf(p -> p.getAge() <= 16);
vim

223

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 Tbyły Useri AuthorizedUserPredicate<User>implementuje IPredicate<T>, to AuthorizedUserPredicate#applyzwraca, 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, flatMapifilter .

Jeśli chcesz po prostu zebrać wszystkich użytkowników, którzy pasują do predykatu, użyj przycisku, Collectorsaby 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.


27
Tak, ale nienawidzę ponownie wymyślać koła ponownie. Wolę znaleźć bibliotekę narzędzi, która działa, kiedy chcę.
Kevin Wong,

2
To nie jest najlepszy sposób, jeśli nie chcesz nowej kolekcji. Użyj metafory iteratora filtrów, która może wprowadzać dane do nowej kolekcji lub może być wszystkim, czego potrzebujesz.
Josh

@Nestor: w rozumieniu Scali, filtrowanie byłoby znacznie prostsze:val authorized = for (user <- users if user.isAuthorized) yield user
Alan

Czy to modyfikuje oryginalną kolekcję, czy tworzy zupełnie nową? Próbowałem użyć tej metody i zarejestrowałem obie moje kolekcje (oryginalną i tę zwróconą z metody), są one takie same. @Alan
Rohan

1
@Rohan, nie ma to na celu mutacji oryginalnej kolekcji. Zauważ, że powyższa kolekcja wyników jest nowo zbudowana, a metoda filtrowania dodaje ją do kolekcji wyników tylko wtedy, gdy ma zastosowanie predykat.
Alan

92

3
to jest w porządku, ale nie jest ogólne, i modyfikuje kolekcję w miejscu (nie jest to miłe)
Kevin Wong

2
Istnieją inne metody filtrowania w CollectionUtils, które nie modyfikują oryginalnej kolekcji.
skaffman

42
W szczególności metoda, która nie modyfikuje kolekcji w miejscu, to org.apache.commons.collections.CollectionUtils # select (Collection, Predicate)
Eero

5
W Commons Commons v4 używa teraz Generics.
Justin Emery

1
Tej metody należy używać ostrożnie, ponieważ polega ona (przynajmniej w implementacji commons-collections-3.2.1) na metodzie iterator.remove (), która jest opcjonalna dla kolekcji, więc zamiast filtrować, powiedzmy, tablicę, możesz uzyskać UnsupportedOperationException.
user2417480,

67

„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.


6
Głosuję za tym: po prostu działa, bez zewnętrznych bibliotek. Nigdy nie zdawałem sobie sprawy, że utworzenie instancji Iteratora może być przydatne w porównaniu do użycia składni For-each lub że możesz usunąć elementy z listy bez ConcurrentModificationException lub czegoś podobnego. :)
ZeroOne,

1
Myślę, że to najlepszy sposób na użycie standardowej biblioteki Java bez kopiowania. W wersji 1.8 pojawiłaby się stream()funkcja, ale nie wszyscy mogą bawić się najnowszymi zabawkami: P
Populus

Czy to również modyfikuje oryginalną kolekcję? @ZeroOne
Rohan

Tak, oczywiście, że tak, @Rohan. Wypróbuj, jeśli w to nie wierzysz. ;)
ZeroOne

Haha zrobiłem! Ale chcę zachować moją oryginalną kolekcję. Czy możesz zasugerować sposób na zrobienie tego bez dodawania zewnętrznej biblioteki? @ZeroOne
Rohan

62

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.


Tak, wiedziałem o lib kolekcji Google. Wersja, której używałem, nie miała w sobie Kolekcje2. Dodałem nową odpowiedź do tego pytania, która wymienia konkretną metodę.
Kevin Wong,

7
Kevin, Iterables.filter () i Iterators.filter () są tam od samego początku i zwykle są wszystkim, czego potrzebujesz.
Kevin Bourrillion

28

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

13
Ugh ... to takie pełne słów. Dlaczego nie mogli tego po prostu zrobić: List <Person> result = personList.filter (p -> p.age> 30);
Kevin Wong,

8
Aby użyć filtru bezpośrednio w kolekcji , musisz użyć removeIf call: download.java.net/jdk8/docs/api/java/util/…
gavenkoa

6
@KevinWong „verbose” właściwie opisuje cały język, który bym pomyślał. Przynajmniej są spójne?
Łotr

5
Dlaczego nie skorzystać z Collectors.toList () w ostatniej części?
Nestor Hernandez Loli

3
Oto link gavenkoa pod warunkiem, że nie 404. personList.removeIf(p -> p.age < 30);Mniej gadatliwy. Słyszałem też o tym, jak zacząć wdrażać api, które akceptują i zwracają Streams zamiast Collections, ponieważ Streamsą bardzo przydatne i szybkie, ale przechodzenie do / z nich jest powolne.
Captain Man,

11

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

11

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 filtermożna znaleźć tutaj .


7

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

2
W porządku, ale wolę implementację Alana, ponieważ zamiast modyfikować, otrzymujesz kopię kolekcji. Co więcej, kod Alana jest bezpieczny dla wątków, podczas gdy twój nie.
marcospereira

7

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



7

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 selectWithmetodą, 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 partitionzwró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.


1
Jak zrobiłbyś a removeIfna liście lub zestawie dla prymitywów?
Vivek Rao,

Interfejs API removeIf został dodany do prymitywnych kolekcji w EC 9.1. eclipse.org/collections/javadoc/9.1.0/org/eclipse/collections/…
Donald Raab

5

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


To całkiem sprytne! Dużo pracy przy implementacji ładnej składni języka Ruby! Negatywne jest to, że twój filtr nie jest funkcją pierwszej klasy i dlatego nie może być ponownie użyty.
Nałóż

Słuszna uwaga. Jednym ze sposobów ponownego wykorzystania treści pętli jest przefaktoryzowanie pętli do metody, która przyjmuje zapytanie wyboru jako parametr. Z pewnością nie jest to jednak tak poręczne i skuteczne jak prawdziwe zamknięcia.
akuhn


5

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

3

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

Połączyć? Nawet jeśli twoja biblioteka jest nieefektywna lub w inny sposób bezużyteczna, interesujące może być sprawdzenie, czy źródło jest dostępne.
MatrixFrog,

Upubliczniono repo ( net-machine.com/indefero/p/jdclib/source/tree/master ). Jesteś zainteresowany pakietem wyrażeń. Pakiet testowy zawiera tester z przykładowym wykorzystaniem. Tak naprawdę nigdy tak naprawdę nie pracowałem nad interfejsem zapytań łańcuchowych, o którym mowa powyżej (nie miałem ochoty pisać prawdziwego parsera), więc droga do jawnego interfejsu zapytań w testerze jest dobrym rozwiązaniem.
jdc0589,

2

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

  • Obsługa właściwości kolekcji (java.util.Collection, java.util.Map i Array).
  • Wsparcie kolekcji wewnątrz kolekcji dowolnej głębokości.
  • Obsługa wewnętrznych zapytań.
  • Obsługa sparametryzowanych zapytań.
  • Może filtrować 1 milion rekordów w ciągu 100 ms.
  • Filtr (zapytanie) jest podawany w prostym formacie json, to jest jak zapytania Mangodb. Oto kilka przykładów.
  • {„id”: {„$ le”: „10”}
    • gdzie właściwość id obiektu jest mniejsza niż 10.
  • {„id”: {„$ in”: [„0”, „100”]}}
    • gdzie właściwość id obiektu to 0 lub 100.
  • {„lineItems”: {„lineAmount”: „1”}}
    • gdzie właściwość kolekcji lineItems sparametryzowanego typu ma lineAmount równe 1.
  • {"$ and": [{"id": "0"}, {"billingAddress": {"city": "DEL"}}]}
    • gdzie właściwość id to 0, a właściwość billingAddress.city to DEL.
  • {„lineItems”: {„podatki”: {„klucz”: {„kod”: „GST”}, „wartość”: {„$ gt”: „1.01”}}}}
    • gdzie właściwość kolekcji lineItems sparametryzowanego typu, która ma typ mapy podatkowej, właściwość sparametryzowanego typu ma kod równy wartości GST większej niż 1,01.
  • {'$ or': [{'code': '10'}, {'skus': {'$ and': [{'price': {'$ in': ['20', '40']} }, {„code”: „RedApple”}]}}]}
    • Wybierz wszystkie produkty, w których kod produktu wynosi 10 lub cena SKU w 20 i 40, a kod SKU to „RedApple”.

1
Powinieneś zrzec się, że jesteś autorem (tak mi się wydaje).
assylias

Tak, jestem autorem tej biblioteki.
Kamran Ali Khan

2

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


2

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

Wystarczy zaimplementować odpowiedni parametr excludeItem na filtr. Skończysz mieć osobne filtry dokładnie tak, jak masz sortowniki w kolekcjach ...
Lawrence

1

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.


1

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

1

Moja odpowiedź opiera się na tej odpowiedzi Kevina Wonga, tutaj jako jednowierszowej, wykorzystującej CollectionUtilsod 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+.


1

Używając java 8, lambda expressionmoż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 productwewnętrznej myProducts, jeśli prod.price>10, to dodaj ten produkt do nowej filtrowanej listy.


1

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

0

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

0

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); 
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.