Guave: Warum gibt es keine Lists.filter () Funktion?

86

Gibt es einen Grund dafür?

Lists.transform()

aber nein

Lists.filter()

?

Wie filtere ich eine Liste richtig? ich könnte benutzen

new ArrayList(Collection2.filter())

Natürlich, aber auf diese Weise kann nicht garantiert werden, dass meine Bestellung gleich bleibt, wenn ich das richtig verstehe.

Fabian Zeindl
quelle
8
Zu Ihrer Information, List.newArrayList (Iterables.filter (...)) ist im Allgemeinen schneller als die neue ArrayList (Collection2.filter (...)). Der ArrayList-Konstruktor ruft size () für die gefilterte Sammlung auf. Für die Berechnung der Größe muss der Filter auf jedes Element der ursprünglichen Liste angewendet werden.
Jared Levy
4
@ JaredLevy Vielleicht sollte es stattdessen List.newArrayList(Iterables.filter(...))sagen Lists.newArrayList(Iterables.filter(...)) .
Abdull

Antworten:

57

Es wurde nicht implementiert, da es eine gefährliche große Anzahl langsamer Methoden wie #get (Index) in der zurückgegebenen Listenansicht (die zu Leistungsfehlern führen) aufdecken würde. Und ListIterator wäre auch ein Problem bei der Implementierung (obwohl ich vor Jahren einen Patch eingereicht habe, um dies zu behandeln).

Da indizierte Methoden in der gefilterten Listenansicht nicht effizient sein können, ist es besser, nur eine gefilterte Iterable zu verwenden, die sie nicht hat.

Dimitris Andreou
quelle
7
Sie gehen davon aus, dass eine Listenansicht zurückgegeben wird. #Filter könnte jedoch so implementiert werden, dass eine neue materialisierte Liste zurückgegeben wird, was eigentlich von einer Filtermethode für Listen erwartet wird, im Gegensatz zu der auf Iterable.
Felix Leipold
@FelixLeipold Dies würde jedoch das Wasser trüben. Wie es ist, filterbedeutet konsequent eine Ansicht (zusammen mit dem Verhalten , das impliziert) , ob das Iterables.filter, Sets.filterusw. Da Iterables.filterverbindet sich leicht mit copyOfauf eine beliebige ImmutableCollection, ich dieses Design Trade-off gut finden (vs mit zusätzlichen Methoden und Namen kommen, wie filteredCopyoder Dingsbums , für Kombinationen einfacher Dienstprogramme).
Luke Usherwood
37

Sie können verwenden Iterables.filter, die definitiv die Bestellung beibehalten wird.

Beachten Sie, dass Sie beim Erstellen einer neuen Liste die Elemente kopieren (natürlich nur Referenzen), sodass keine Live-Ansicht der ursprünglichen Liste angezeigt wird. Das Erstellen einer Ansicht wäre ziemlich schwierig - bedenken Sie diese Situation:

Predicate<StringBuilder> predicate = 
    /* predicate returning whether the builder is empty */
List<StringBuilder> builders = Lists.newArrayList();
List<StringBuilder> view = Lists.filter(builders, predicate);

for (int i = 0; i < 10000; i++) {
    builders.add(new StringBuilder());
}
builders.get(8000).append("bar");

StringBuilder firstNonEmpty = view.get(0);

Das müsste über die gesamte ursprüngliche Liste iterieren und den Filter auf alles anwenden. Ich nehme an, es könnte erforderlich sein, dass sich die Prädikatübereinstimmung während der Lebensdauer der Ansicht nicht geändert hat, aber das wäre nicht ganz zufriedenstellend.

(Dies ist nur eine Vermutung, wohlgemerkt. Vielleicht mischt sich einer der Guava-Betreuer mit dem wahren Grund ein :)

Jon Skeet
quelle
1
Collections2.filter.iteratorruft nur an Iterables.filter, das Ergebnis ist also das gleiche.
Skaffman
@skaffman: In diesem Fall würde ich die Iterables.filterVersion nur aus Gründen der Klarheit verwenden.
Jon Skeet
3
... es sei denn, Sie benötigen einen view.size()späteren Code :)
Xaerxess
28

Ich könnte new List(Collection2.filter())natürlich verwenden, aber auf diese Weise kann nicht garantiert werden, dass meine Bestellung gleich bleibt.

Das ist nicht wahr. Collections2.filter()ist eine träge evaluierte Funktion - sie filtert Ihre Sammlung erst, wenn Sie auf die gefilterte Version zugreifen. Wenn Sie beispielsweise über die gefilterte Version iterieren, werden die gefilterten Elemente in derselben Reihenfolge wie Ihre ursprüngliche Sammlung aus dem Iterator entfernt (abzüglich der offensichtlich herausgefilterten).

Vielleicht haben Sie gedacht, dass es die Filterung im Voraus durchführt und dann die Ergebnisse in eine willkürliche, ungeordnete Sammlung irgendeiner Form speichert - das tut es nicht.

Also , wenn Sie die Ausgabe verwenden , Collections2.filter()als die Eingabe in eine neue Liste, dann wird Ihre ursprüngliche Reihenfolge wird beibehalten.

Mit statischen Importen (und der Lists.newArrayListFunktion) wird es ziemlich prägnant:

List filteredList = newArrayList(filter(originalList, predicate));

Beachten Sie, dass während Collections2.filternicht eifrig Iterierte über die zugrunde liegende Auflistung, Lists.newArrayList wird - es werden alle Elemente des gefilterten Sammlung extrahieren und kopieren Sie sie in eine neue ArrayList.

Skaffman
quelle
Es ist eher wie: List filteredList = newArrayList(filter(originalList, new Predicate<T>() { @Override public boolean apply(T input) { return (...); } }));oder dh. List filteredList = newArrayList(filter(originalList, Predicates.notNull()));
Xaerxess
@ Xaerxess: Ups, ja, habe das Prädikat vergessen ... behoben
Skaffman
@Bozho: Danke ... hat lange genug
gedauert
12

Wie von Jon erwähnt, können Sie verwenden Iterables.filter(..)oder Collections2.filter(..)und wenn Sie keine Live-Ansicht benötigen, können Sie verwenden ImmutableList.copyOf(Iterables.filter(..))oder Lists.newArrayList( Iterables.filter(..))und ja, die Bestellung wird beibehalten.

Wenn Sie wirklich daran interessiert sind, warum Teil, können Sie https://github.com/google/guava/issues/505 für weitere Details besuchen .

Premraj
quelle
6

Zusammenfassend können Sie einen generischen Wrapper zum Filtern von Listen erstellen:

public static <T> List<T> filter(Iterable<T> userLists, Predicate<T> predicate) {
    return Lists.newArrayList(Iterables.filter(userLists, predicate));
}
Holger Brandl
quelle