Guava: Perché non esiste la funzione Lists.filter ()?


86

C'è una ragione c'è

Lists.transform()

ma no

Lists.filter()

?

Come faccio a filtrare correttamente un elenco? Potrei usare

new ArrayList(Collection2.filter())

certo, ma in questo modo non è garantito che il mio ordine rimanga lo stesso, se ho capito bene.


8
Cordiali saluti, List.newArrayList (Iterables.filter (...)) è generalmente più veloce di new ArrayList (Collection2.filter (...)). Il costruttore di ArrayList chiama size () sulla raccolta filtrata e il calcolo della dimensione richiede che il filtro venga applicato a ogni elemento dell'elenco originale.
Jared Levy

4
@ JaredLevy Forse invece di List.newArrayList(Iterables.filter(...)), dovrebbe dire Lists.newArrayList(Iterables.filter(...)) .
Abdull

Risposte:


57

Non è stato implementato perché esporrebbe un numero elevato di metodi lenti, come #get (index) nella visualizzazione Elenco restituita (invitando bug di prestazioni). E anche ListIterator sarebbe un problema da implementare (anche se ho presentato una patch anni fa per coprirlo).

Poiché i metodi indicizzati non possono essere efficienti nella visualizzazione elenco filtrata, è meglio utilizzare un Iterable filtrato, che non li ha.


7
Stai assumendo che verrebbe restituita una visualizzazione elenco. Tuttavia, #filter potrebbe essere implementato come restituzione di un nuovo elenco materializzato, che è in realtà ciò che mi aspetterei da un metodo di filtro per elenchi rispetto a quello su Iterable.
Felix Leipold

@FelixLeipold Ciò tuttavia infangerebbe le acque. Così com'è, filtersignifica costantemente una vista (insieme al comportamento che implica) che sia Iterables.filter, Sets.filterecc. Dal momento che si Iterables.filtercombina facilmente con copyOfsu qualsiasi ImmutableCollection, trovo che questo sia un buon compromesso di progettazione (rispetto a metodi e nomi extra, come filteredCopyo altro , per combinazioni di semplici utilità).
Luke Usherwood

37

Puoi usare Iterables.filter, che manterrà sicuramente l'ordine.

Nota che costruendo un nuovo elenco, copierai gli elementi (solo i riferimenti, ovviamente), quindi non sarà una vista dal vivo nell'elenco originale. Creare una vista sarebbe piuttosto complicato: considera questa situazione:

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

Ciò dovrebbe iterare sull'intero elenco originale, applicando il filtro a tutto. Suppongo che potrebbe richiedere che la corrispondenza del predicato non sia cambiata nel corso della vita della vista, ma ciò non sarebbe del tutto soddisfacente.

(Questo è solo un indovinare, intendiamoci. Forse uno dei manutentori di Guava contribuirà con la vera ragione :)


1
Collections2.filter.iteratorchiama Iterables.filter, quindi il risultato è lo stesso.
skaffman

@skaffman: nel qual caso userei la Iterables.filterversione solo per chiarezza.
Jon Skeet

3
... a meno che tu non abbia bisogno di un view.size()posto più avanti nel codice :)
Xaerxess

28

Potrei usare new List(Collection2.filter())ovviamente, ma in questo modo non è garantito che il mio ordine rimanga lo stesso.

Questo non è vero. Collections2.filter()è una funzione pigramente valutata: in realtà non filtra la tua raccolta finché non inizi ad accedere alla versione filtrata. Ad esempio, se iterate sulla versione filtrata, gli elementi filtrati appariranno dall'iteratore nello stesso ordine della vostra raccolta originale (meno quelli filtrati, ovviamente).

Forse stavi pensando che fa il filtraggio in primo piano, quindi scarica i risultati in una Raccolta arbitraria e non ordinata di qualche forma - non è così.

Quindi, se utilizzi l'output di Collections2.filter()come input per un nuovo elenco, l'ordine originale verrà mantenuto.

Usando le importazioni statiche (e la Lists.newArrayListfunzione), diventa abbastanza succinto:

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

Nota che, mentre Collections2.filternon sarà avidamente iterate sulla raccolta sottostante, Lists.newArrayList sarà - sarà estrarre tutti gli elementi della collezione filtrato e copiarli in un nuovo ArrayList.


È più come: List filteredList = newArrayList(filter(originalList, new Predicate<T>() { @Override public boolean apply(T input) { return (...); } }));o ie. List filteredList = newArrayList(filter(originalList, Predicates.notNull()));
Xaerxess

@Xaerxess: Oops, sì, ho dimenticato il predicato ... risolto
skaffman

@ Bozho: Grazie ... mi ci è voluto abbastanza :)
skaffman

12

Come accennato da Jon, è possibile utilizzare Iterables.filter(..)o Collections2.filter(..), e se non avete bisogno di una visione dal vivo è possibile utilizzare ImmutableList.copyOf(Iterables.filter(..))o Lists.newArrayList( Iterables.filter(..))e sì ordinazione sarà mantenuto.

Se sei davvero interessato al perché parte, puoi visitare https://github.com/google/guava/issues/505 per maggiori dettagli.


6

Riassumendo ciò che hanno detto gli altri, puoi facilmente creare un wrapper generico per filtrare gli elenchi:

public static <T> List<T> filter(Iterable<T> userLists, Predicate<T> predicate) {
    return Lists.newArrayList(Iterables.filter(userLists, predicate));
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.