Was ist der beste Weg, um eine Java-Sammlung zu filtern?

671

Ich möchte ein java.util.Collectionbasierend auf einem Prädikat filtern .

Kevin Wong
quelle

Antworten:

698

Java 8 ( 2014 ) löst dieses Problem mithilfe von Streams und Lambdas in einer Codezeile:

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

Hier ist ein Tutorial .

Verwenden Sie Collection#removeIfdiese Option, um die vorhandene Sammlung zu ändern. (Hinweis: In diesem Fall entfernt das Prädikat Objekte, die das Prädikat erfüllen.)

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

Mit Lambdaj können Sammlungen gefiltert werden, ohne Schleifen oder innere Klassen zu schreiben:

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

Können Sie sich etwas Lesbareres vorstellen?

Haftungsausschluss: Ich bin ein Mitwirkender bei Lambdaj

Mario Fusco
quelle
34
Schön, aber die statischen Importe verschleiern, was los ist. Als Referenz wählen Sie / haben / ein sind statische Importe auf ch.lambdaj.Lambda, größer als org.hamcrest.Matchers
MikePatel
11
LambdaJ ist wirklich sexy, aber es ist erwähnenswert, dass es einen erheblichen Overhead (Durchschnitt 2,6) bedeutet: code.google.com/p/lambdaj/wiki/PerformanceAnalysis .
Doc Davluz
7
Anscheinend funktioniert nicht auf Android: groups.google.com/forum/#!msg/lambdaj/km7uFgvSd3k/grJhgl3ik5sJ
Moritz
7
Wirklich wie dieses Beispiel von LamdaJ ... ähnlich den in .NET integrierten Lambda-Funktionen. Und wo kann eine Person im Alter von 16 Jahren trinken? Wir sollten erwägen, eine Lokalisierungsbeschränkung hinzuzufügen. : P
MAbraham1
3
removeWenn Beispiel sein solltepersons.removeIf(p -> p.getAge() <= 16);
vim
223

Angenommen, Sie verwenden Java 1.5 und können keine Google-Sammlungen hinzufügen , würde ich etwas sehr Ähnliches tun wie die Google-Mitarbeiter. Dies ist eine leichte Variation von Jons Kommentaren.

Fügen Sie diese Schnittstelle zuerst Ihrer Codebasis hinzu.

public interface IPredicate<T> { boolean apply(T type); }

Die Implementierer können antworten, wenn ein bestimmtes Prädikat für einen bestimmten Typ gilt. ZB wenn Twären UserundAuthorizedUserPredicate<User> implementiert IPredicate<T>, dann AuthorizedUserPredicate#applykehrt ob das übergebene in Userautorisiert ist.

Dann könnte man in einer Utility-Klasse sagen

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

Angenommen, Sie haben die oben genannte Verwendung

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

Wenn die Leistung bei der linearen Prüfung von Bedeutung ist, möchte ich möglicherweise ein Domänenobjekt mit der Zielsammlung haben. Das Domänenobjekt mit der Zielsammlung verfügt über eine Filterlogik für die Methoden, mit denen die Zielsammlung initialisiert, hinzugefügt und festgelegt wird.

AKTUALISIEREN:

In der Utility-Klasse (sagen wir Prädikat) habe ich eine Auswahlmethode mit einer Option für den Standardwert hinzugefügt, wenn das Prädikat nicht den erwarteten Wert zurückgibt, sowie eine statische Eigenschaft für Parameter, die innerhalb des neuen IPredicate verwendet werden sollen.

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

Im folgenden Beispiel wird nach fehlenden Objekten zwischen Sammlungen gesucht:

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

Im folgenden Beispiel wird nach einer Instanz in einer Sammlung gesucht und das erste Element der Sammlung als Standardwert zurückgegeben, wenn die Instanz nicht gefunden wird:

MyType myObject = Predicate.select(collectionOfMyType, new IPredicate<MyType>() {
public boolean apply(MyType objectOfMyType) {
    return objectOfMyType.isDefault();
}}, collectionOfMyType.get(0));

UPDATE (nach Java 8):

Es ist einige Jahre her, seit ich (Alan) diese Antwort zum ersten Mal gepostet habe, und ich kann immer noch nicht glauben, dass ich SO-Punkte für diese Antwort sammle. Auf jeden Fall wäre meine Antwort jetzt, da Java 8 die Sprache geschlossen hat, erheblich anders und einfacher. Unter Java 8 ist keine eindeutige statische Dienstprogrammklasse erforderlich. Wenn Sie also das erste Element finden möchten, das Ihrem Prädikat entspricht.

final UserService userService = ... // perhaps injected IoC
final Optional<UserModel> userOption = userCollection.stream().filter(u -> {
    boolean isAuthorized = userService.isAuthorized(u);
    return isAuthorized;
}).findFirst();

Die JDK 8 API für optionals hat die Fähigkeit zu get(), isPresent(), orElse(defaultUser), orElseGet(userSupplier)und orElseThrow(exceptionSupplier), wie auch andere ‚monadischen‘ Funktionen, wie map, flatMapundfilter .

Wenn Sie einfach alle Benutzer erfassen möchten, die dem Prädikat entsprechen, Collectorsbeenden Sie den Stream in der gewünschten Sammlung mit.

final UserService userService = ... // perhaps injected IoC
final List<UserModel> userOption = userCollection.stream().filter(u -> {
    boolean isAuthorized = userService.isAuthorized(u);
    return isAuthorized;
}).collect(Collectors.toList());

Sehen Sie hier für weitere Beispiele, wie Java 8 - Streams zu arbeiten.

Alan
quelle
27
Ja, aber ich hasse es, das Rad immer wieder neu zu erfinden. Ich würde lieber eine Utility-Bibliothek finden, die funktioniert, wenn ich will.
Kevin Wong
2
Dies ist nicht der beste Weg, falls Sie die neue Kollektion nicht möchten. Verwenden Sie die Filter-Iterator-Metapher, die möglicherweise in eine neue Sammlung eingegeben wird, oder es ist alles, was Sie benötigen.
Josh
@Nestor: In einem Scala-Verständnis wäre das Filtern viel einfacher:val authorized = for (user <- users if user.isAuthorized) yield user
Alan
Ändert dies die ursprüngliche Kollektion oder erstellt sie eine brandneue? Ich habe versucht, diese Methode zu verwenden, und beide meine Sammlungen (die ursprüngliche und die von der Methode zurückgegebene) protokolliert. Sie sind identisch. @ Alan
Rohan
1
@Rohan, dies soll nicht die ursprüngliche Sammlung mutieren. Beachten Sie, dass die obige Ergebnissammlung neu erstellt wurde und die Filtermethode nur dann zur Ergebnissammlung hinzugefügt wird, wenn das Prädikat gilt.
Alan
92

Verwenden Sie CollectionUtils.filter (Collection, Predicate) von Apache Commons.

Kevin Wong
quelle
3
Dies ist in Ordnung, aber es ist kein generisches und ändert die Sammlung an Ort und Stelle (nicht schön)
Kevin Wong
2
Es gibt andere Filtermethoden in CollectionUtils, die die ursprüngliche Sammlung nicht ändern.
Skaffman
42
Insbesondere ist die Methode, die die vorhandene Sammlung nicht ändert, org.apache.commons.collections.CollectionUtils # select (Sammlung, Prädikat)
Eero
5
In Commons Collections v4 werden jetzt Generics verwendet.
Justin Emery
1
Diese Methode sollte mit Vorsicht verwendet werden, da sie (zumindest bei der Implementierung von commons-collection-3.2.1) auf der iterator.remove () -Methode beruht, die für die Sammlungen optional ist. Sie können also beispielsweise beispielsweise ein Array filtern Holen Sie sich eine UnsupportedOperationException.
user2417480
67

"Bester" Weg ist eine zu breite Anfrage. Ist es "am kürzesten"? "Am schnellsten"? "Lesbar"? An Ort und Stelle oder in eine andere Sammlung filtern?

Der einfachste (aber nicht am besten lesbare) Weg besteht darin, ihn zu iterieren und die Methode Iterator.remove () zu verwenden:

Iterator<Foo> it = col.iterator();
while( it.hasNext() ) {
  Foo foo = it.next();
  if( !condition(foo) ) it.remove();
}

Um die Lesbarkeit zu verbessern, können Sie es jetzt in eine Dienstprogrammmethode einbinden. Erfinden Sie dann eine IPredicate-Schnittstelle, erstellen Sie eine anonyme Implementierung dieser Schnittstelle und führen Sie Folgendes aus:

CollectionUtils.filterInPlace(col,
  new IPredicate<Foo>(){
    public boolean keepIt(Foo foo) {
      return foo.isBar();
    }
  });

Dabei filtert filterInPlace () die Auflistung und ruft Predicate.keepIt () auf, um zu erfahren, ob die Instanz in der Auflistung beibehalten werden soll.

Ich sehe keine Rechtfertigung dafür, eine Bibliothek eines Drittanbieters nur für diese Aufgabe einzubringen.

Vladimir Dyuzhev
quelle
6
Meine Stimme geht für diese: Es funktioniert einfach, ohne externe Bibliotheken. Ich hätte nie gedacht, dass das Instanziieren eines Iterators im Vergleich zur Verwendung der For-Each-Syntax tatsächlich nützlich sein könnte oder dass Sie Elemente ohne ConcurrentModificationException oder ähnliches aus einer Liste entfernen könnten. :)
ZeroOne
1
Ich denke, dies ist der beste Weg, um die Standard-Java-Bibliothek ohne Kopieren zu verwenden. Für 1.8 würde es das stream()Feature geben, aber nicht jeder kann mit den neuesten Spielzeugen spielen: P
Populus
Ändert dies auch die ursprüngliche Sammlung? @ ZeroOne
Rohan
Ja natürlich, @Rohan. Probieren Sie es aus, wenn Sie es nicht glauben. ;)
ZeroOne
Haha, ich habe es getan! Aber ich möchte meine ursprüngliche Sammlung behalten. Können Sie einen Weg vorschlagen, dies zu tun, ohne eine externe Bibliothek hinzuzufügen? @ ZeroOne
Rohan
62

Betrachten Sie Google Collections für ein aktualisiertes Collections-Framework, das Generika unterstützt.

UPDATE : Die Google-Sammlungsbibliothek ist jetzt veraltet. Sie sollten stattdessen die neueste Version von Guava verwenden. Es verfügt weiterhin über dieselben Erweiterungen des Sammlungsframeworks, einschließlich eines Mechanismus zum Filtern basierend auf einem Prädikat.

Heidegrenzen
quelle
Ja, ich wusste über die Google-Sammlungen lib. Die von mir verwendete Version enthielt keine Collections2. Ich habe dieser Frage eine neue Antwort hinzugefügt, in der die spezifische Methode aufgeführt ist.
Kevin Wong
7
Kevin, Iterables.filter () und Iterators.filter () waren von Anfang an dabei und sind normalerweise alles, was Sie brauchen.
Kevin Bourrillion
28

Warten Sie auf Java 8:

List<Person> olderThan30 = 
  //Create a Stream from the personList
  personList.stream().
  //filter the element to select only those with age >= 30
  filter(p -> p.age >= 30).
  //put those filtered elements into a new List.
  collect(Collectors.toList());
Gavenkoa
quelle
13
Ugh ... es ist so ausführlich. Warum konnten sie nicht einfach Folgendes tun: List <Person> result = personList.filter (p -> p.age> 30);
Kevin Wong
8
Um den Filter direkt in der Sammlung zu verwenden , müssen Sie removeIf verwenden. Rufen Sie auf: download.java.net/jdk8/docs/api/java/util/…
gavenkoa
6
@ KevinWong "ausführlich" beschreibt ziemlich genau die ganze Sprache, die ich denken würde. Zumindest sind sie konsequent?
Schurke
5
Warum nicht im letzten Teil Collectors.toList () verwenden?
Nestor Hernandez Loli
3
Hier ist ein Link gavenkoa, der nicht 404 ist. personList.removeIf(p -> p.age < 30);Weniger ausführlich. Außerdem habe ich gehört, dass mit der Implementierung von APIs begonnen wird, die Streams anstelle von Collections akzeptieren und zurückgeben, da Streams sehr nützlich und schnell sind, aber das Gehen zu / von ihnen langsam ist.
Captain Man
11

Seit der frühen Version von Java 8 können Sie Folgendes ausprobieren:

Collection<T> collection = ...;
Stream<T> stream = collection.stream().filter(...);

Wenn Sie beispielsweise eine Liste von Ganzzahlen hatten und die Zahlen filtern möchten, die> 10 sind, und diese Zahlen dann auf der Konsole ausdrucken möchten, können Sie Folgendes tun:

List<Integer> numbers = Arrays.asList(12, 74, 5, 8, 16);
numbers.stream().filter(n -> n > 10).forEach(System.out::println);
Josh M.
quelle
11

Ich werde RxJava in den Ring werfen , der auch für Android verfügbar ist . RxJava ist möglicherweise nicht immer die beste Option, bietet Ihnen jedoch mehr Flexibilität, wenn Sie Ihrer Sammlung mehr Transformationen hinzufügen oder Fehler beim Filtern behandeln möchten.

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

Ausgabe:

1
3
5

Weitere Details zu RxJava filterfinden Sie hier .

Brian Bowman
quelle
7

Die Einrichtung:

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

Die Verwendung:

List<MyObject> myList = ...;
filterCollection(myList, new Predicate<MyObject>() {
  public boolean filter(MyObject obj) {
    return obj.shouldFilter();
  }
});
jon
quelle
2
Gut, aber ich bevorzuge die Implementierung von Alan, da Sie eine Kopie der Sammlung erhalten, anstatt sie zu ändern. Darüber hinaus ist Alans Code threadsicher, Ihr Code jedoch nicht.
Marcospereira
7

Wie wäre es mit einem einfachen und geradlinigen Java

 List<Customer> list ...;
 List<Customer> newList = new ArrayList<>();
 for (Customer c : list){
    if (c.getName().equals("dd")) newList.add(c);
 }

Einfach, lesbar und leicht (und funktioniert in Android!). Wenn Sie jedoch Java 8 verwenden, können Sie dies in einer süßen Zeile tun:

List<Customer> newList = list.stream().filter(c -> c.getName().equals("dd")).collect(toList());

Beachten Sie, dass toList () statisch importiert wird

Nestor Hernandez Loli
quelle
7

Schauen wir uns an, wie Sie eine integrierte JDK-Liste und eine MutableList mithilfe von Eclipse-Sammlungen filtern .

List<Integer> jdkList = Arrays.asList(1, 2, 3, 4, 5);
MutableList<Integer> ecList = Lists.mutable.with(1, 2, 3, 4, 5);

Wenn Sie Zahlen unter 3 filtern möchten, erwarten Sie die folgenden Ausgaben.

List<Integer> selected = Lists.mutable.with(1, 2);
List<Integer> rejected = Lists.mutable.with(3, 4, 5);

Hier erfahren Sie, wie Sie mit einem Java 8-Lambda filtern können 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));

Hier erfahren Sie, wie Sie mithilfe einer anonymen inneren Klasse filtern können 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));

Hier sind einige Alternativen zum Filtern von JDK-Listen und Eclipse Collections MutableLists mithilfe der Predicates- Factory.

Assert.assertEquals(selected, Iterate.select(jdkList, Predicates.lessThan(3)));
Assert.assertEquals(selected, ecList.select(Predicates.lessThan(3)));

Hier ist eine Version, die dem Prädikat kein Objekt zuweist, indem stattdessen die Predicates2- Factory mit der selectWithMethode verwendet wird, die a verwendet Predicate2.

Assert.assertEquals(
    selected, ecList.selectWith(Predicates2.<Integer>lessThan(), 3));

Manchmal möchten Sie nach einem negativen Zustand filtern. In Eclipse Collections gibt es eine spezielle Methode für diesen Aufruf reject.

Assert.assertEquals(rejected, Iterate.reject(jdkList, lessThan3));
Assert.assertEquals(rejected, ecList.reject(lessThan3));

Die Methode partitiongibt zwei Sammlungen zurück, die die Elemente enthalten, die von der ausgewählt und von der abgelehnt wurden 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());

Hinweis: Ich bin ein Committer für Eclipse-Sammlungen.

Donald Raab
quelle
1
Wie würden Sie removeIfeine Liste erstellen oder Primitive festlegen?
Vivek Rao
Die API für removeIf wurde in EC 9.1 zu primitiven Sammlungen hinzugefügt. eclipse.org/collections/javadoc/9.1.0/org/eclipse/collections/…
Donald Raab
5

Mit dem ForEach DSL können Sie schreiben

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

Bei einer Sammlung von [Der, schnelle, braune, Fuchs, springt, über, der, faule, Hund] ergibt sich [schnell, braun, springt, über, faul], dh alle Zeichenfolgen sind länger als drei Zeichen.

Alle von ForEach DSL unterstützten Iterationsstile sind

  • AllSatisfy
  • AnySatisfy
  • Collect
  • Counnt
  • CutPieces
  • Detect
  • GroupedBy
  • IndexOf
  • InjectInto
  • Reject
  • Select

Weitere Informationen finden Sie unter https://www.iam.unibe.ch/scg/svn_repos/Sources/ForEach

akuhn
quelle
Das ist ziemlich klug! Viel Arbeit, um eine nette Ruby-ish-Syntax zu implementieren! Das Negative ist, dass Ihr Filter keine erstklassige Funktion ist und daher nicht wiederverwendet werden kann. Roll on Verschlüsse ...
oxbow_lakes
Guter Punkt. Eine Möglichkeit, den Schleifenkörper wiederzuverwenden, besteht darin, die Schleife in eine Methode umzuwandeln, die die Auswahlabfrage als Parameter verwendet. Das ist jedoch bei weitem nicht so praktisch und leistungsstark wie echte Verschlüsse.
Akuhn
5

Da Java 9 Collectors.filtering aktiviert ist:

public static <T, A, R>
    Collector<T, ?, R> filtering(Predicate<? super T> predicate,
                                 Collector<? super T, A, R> downstream)

Daher sollte die Filterung wie folgt sein:

collection.stream().collect(Collectors.filtering(predicate, collector))

Beispiel:

List<Integer> oddNumbers = List.of(1, 19, 15, 10, -10).stream()
            .collect(Collectors.filtering(i -> i % 2 == 1, Collectors.toList()));
Yanefedor
quelle
3

Dies, zusammen mit dem Mangel an echten Abschlüssen, ist mein größter Kritikpunkt für Java. Ehrlich gesagt sind die meisten der oben genannten Methoden ziemlich einfach zu lesen und WIRKLICH effizient. Nachdem Sie jedoch Zeit mit .Net, Erlang usw. verbracht haben, macht das auf Sprachebene integrierte Listenverständnis alles so viel sauberer. Ohne Ergänzungen auf Sprachebene kann Java nicht so sauber sein wie viele andere Sprachen in diesem Bereich.

Wenn die Leistung ein großes Problem darstellt, sind Google-Sammlungen der richtige Weg (oder schreiben Sie Ihr eigenes einfaches Prädikat-Dienstprogramm). Die Lambdaj-Syntax ist für manche Menschen besser lesbar, aber nicht ganz so effizient.

Und dann gibt es eine Bibliothek, die ich geschrieben habe. Ich werde alle Fragen bezüglich seiner Effizienz ignorieren (ja, es ist so schlimm) ...... Ja, ich weiß, dass es eindeutig auf Reflexion basiert, und nein, ich benutze es nicht wirklich, aber es funktioniert:

LinkedList<Person> list = ......
LinkedList<Person> filtered = 
           Query.from(list).where(Condition.ensure("age", Op.GTE, 21));

ODER

LinkedList<Person> list = ....
LinkedList<Person> filtered = Query.from(list).where("x => x.age >= 21");
jdc0589
quelle
Verknüpfung? Selbst wenn Ihre Bibliothek ineffizient oder anderweitig unbrauchbar ist, kann es interessant sein, zu prüfen, ob die Quelle verfügbar ist.
MatrixFrog
Das Repo wurde veröffentlicht ( net-machine.com/indefero/p/jdclib/source/tree/master ). Sie interessieren sich für das Ausdruckspaket. Das Testpaket enthält einen Tester mit Beispielverwendung. Ich habe nie wirklich viel an der oben genannten String-Abfrage-Oberfläche gearbeitet (ich hatte keine Lust, einen echten Parser zu schreiben), daher ist die explizite Abfrage-Oberfläche im Tester der richtige Weg.
jdc0589
2

JFilter http://code.google.com/p/jfilter/ ist am besten für Ihre Anforderung geeignet.

JFilter ist eine einfache und leistungsstarke Open Source-Bibliothek zum Abfragen der Sammlung von Java-Beans.

Hauptmerkmale

  • Unterstützung der Sammlungseigenschaften (java.util.Collection, java.util.Map und Array).
  • Unterstützung der Sammlung innerhalb der Sammlung jeder Tiefe.
  • Unterstützung innerer Fragen.
  • Unterstützung von parametrisierten Abfragen.
  • Kann 1 Million Datensätze in wenigen 100 ms filtern.
  • Filter (Abfrage) wird im einfachen JSON-Format angegeben, es ist wie bei Mangodb-Abfragen. Es folgen einige Beispiele.
  • {"id": {"$ le": "10"}
    • Dabei ist die Objekt-ID-Eigenschaft kleiner als gleich 10.
  • {"id": {"$ in": ["0", "100"]}}
    • Dabei ist die Objekt-ID-Eigenschaft 0 oder 100.
  • {"lineItems": {"lineAmount": "1"}}
    • Dabei hat lineAtems-Auflistungseigenschaft vom parametrisierten Typ lineAmount gleich 1.
  • {"$ und": [{"id": "0"}, {"billingAddress": {"city": "DEL"}}]}
    • Dabei ist die Eigenschaft id 0 und die Eigenschaft billingAddress.city DEL.
  • {"lineItems": {"tax": {"key": {"code": "GST"}, "value": {"$ gt": "1.01"}}}
    • Dabei hat die LineItems-Auflistungseigenschaft des parametrisierten Typs mit der Tax-Map-Typ-Eigenschaft des parametrisierten Typs einen Code, der einem GST-Wert größer als 1,01 entspricht.
  • {'$ or': [{'code': '10'}, {'skus': {'$ und': [{'price': {'$ in': ['20', '40']} }, {'code': 'RedApple'}]}}]}
    • Wählen Sie alle Produkte aus, bei denen der Produktcode 10 oder der Sku-Preis in 20 und 40 lautet und der Sku-Code "RedApple" lautet.
Kamran Ali Khan
quelle
1
Sie sollten ablehnen, dass Sie der Autor sind (wie ich denke, dass dies der Fall ist).
Assylias
Ja, ich bin der Autor dieser Bibliothek.
Kamran Ali Khan
2

Ich habe eine erweiterte Iterable-Klasse geschrieben , die das Anwenden funktionaler Algorithmen unterstützt, ohne den Sammlungsinhalt zu kopieren.

Verwendungszweck:

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

Der obige Code wird tatsächlich ausgeführt

for( int n : myList )
{
    if( n % 2 == 0 ) 
    {
        System.out.println(n);
    }
}
Vincent Robert
quelle
2

Einige wirklich tolle tolle Antworten hier. Ich möchte Thins so einfach und lesbar wie möglich halten:

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();
                }
            }
        }
    }
}
Lawrence
quelle
Implementieren Sie einfach das richtige excludeItem pro Filter. Sie werden am Ende separate Filter haben, genau wie Sie Sortierer in Sammlungen haben ...
Lawrence
1

Die einfache Lösung vor Java8:

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

Leider ist diese Lösung nicht vollständig generisch und gibt eher eine Liste als den Typ der angegebenen Sammlung aus. Das Einbringen von Bibliotheken oder das Schreiben von Funktionen, die diesen Code umschließen, erscheint mir außerdem übertrieben, es sei denn, die Bedingung ist komplex, aber dann können Sie eine Funktion für die Bedingung schreiben.

Andrew McKnight
quelle
1

https://code.google.com/p/joquery/

Unterstützt verschiedene Möglichkeiten,

Gegebene Sammlung,

Collection<Dto> testList = new ArrayList<>();

vom Typ,

class Dto
{
    private int id;
    private String text;

    public int getId()
    {
        return id;
    }

    public int getText()
    {
        return text;
    }
}

Filter

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

Ebenfalls,

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

Sortieren (auch für Java 7 verfügbar)

Filter<Dto> query = CQ.<Dto>filter(testList)
        .orderBy()
        .property(Dto::getId)
        .property(Dto::getName)
    Collection<Dto> sorted = query.list();

Gruppierung (auch für Java 7 verfügbar)

GroupQuery<Integer,Dto> query = CQ.<Dto,Dto>query(testList)
        .group()
        .groupBy(Dto::getId)
    Collection<Grouping<Integer,Dto>> grouped = query.list();

Joins (auch für Java 7 verfügbar)

Gegeben,

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

Kann verbunden werden wie,

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

Ausdrücke

Filter<Dto> query = CQ.<Dto>filter()
    .from(testList)
    .where()
    .exec(s -> s.getId() + 1).eq().value(2);
Niedrig fliegender Pelikan
quelle
1

Meine Antwort baut auf dem von Kevin Wong, hier als Einzeiler unter Verwendung CollectionUtilsvon Feder und einem Java 8 Lambda - Ausdruck.

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

Dies ist so präzise und lesbar wie jede Alternative, die ich gesehen habe (ohne aspektbasierte Bibliotheken zu verwenden).

Spring CollectionUtils ist ab Spring Version 4.0.2.RELEASE verfügbar. Denken Sie daran, dass Sie JDK 1.8 und Sprachstufe 8+ benötigen.

vikingsteve
quelle
1

Mit java 8speziell , lambda expressionkönnen Sie es einfach tun , wie das folgende Beispiel:

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

Wenn für jede productinterne myProductsSammlung, wenn prod.price>10, dann fügen Sie dieses Produkt der neuen gefilterten Liste hinzu.

hd84335
quelle
1

Ich musste eine Liste abhängig von den Werten filtern, die bereits in der Liste vorhanden sind. Entfernen Sie beispielsweise alle folgenden Werte, die kleiner als der aktuelle Wert sind. {2 5 3 4 7 5} -> {2 5 7}. Oder um beispielsweise alle Duplikate zu entfernen {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;
    }
}

Dies wird so verwendet.

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;
    }
});
Fredrik Metcalf
quelle
0

Mit Guave:

Collection<Integer> collection = Lists.newArrayList(1, 2, 3, 4, 5);

Iterators.removeIf(collection.iterator(), new Predicate<Integer>() {
    @Override
    public boolean apply(Integer i) {
        return i % 2 == 0;
    }
});

System.out.println(collection); // Prints 1, 3, 5
ZhekaKozlov
quelle
0

In Java 8 können Sie diese Filtermethode direkt verwenden und dies dann tun.

 List<String> lines = Arrays.asList("java", "pramod", "example");

 List<String> result = lines.stream()              
         .filter(line -> !"pramod".equals(line))     
         .collect(Collectors.toList());              

 result.forEach(System.out::println); 
pramod_m
quelle