web-dev-qa-db-fra.com

Goyave: Pourquoi n'y a-t-il pas de fonction Lists.filter ()?

Y a-t-il une raison

Lists.transform()

mais non

Lists.filter()

?

Comment filtrer une liste correctement? je pourrais utiliser

new ArrayList(Collection2.filter())

bien sûr, mais de cette façon, il n'est pas garanti que ma commande reste la même, si je comprends bien.

86
Fabian Zeindl

Il n'a pas été implémenté car il exposerait un grand nombre périlleux de méthodes lentes, telles que #get (index) dans la vue List retournée (invitant des bogues de performance). Et ListIterator serait également difficile à implémenter (même si j'ai soumis un correctif ans pour couvrir cela).

Étant donné que les méthodes indexées ne peuvent pas être efficaces dans la vue Liste filtrée, il est préférable de simplement utiliser un Iterable filtré, qui ne les a pas.

57
Dimitris Andreou

Vous pouvez utiliser Iterables.filter, qui maintiendra définitivement la commande.

Notez qu'en construisant une nouvelle liste, vous serez copiant les éléments (juste des références, bien sûr) - donc ce ne sera pas une vue en direct sur la liste d'origine. La création d'une vue serait assez délicate - considérez cette 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);

Cela devrait parcourir toute la liste d'origine, en appliquant le filtre à tout. Je suppose que cela pourrait nécessiter que la correspondance des prédicats ne change pas pendant la durée de vie de la vue, mais ce ne serait pas entièrement satisfaisant.

(C'est juste une supposition, remarquez. Peut-être que l'un des responsables de la goyave va intégrer la vraie raison :)

37
Jon Skeet

Je pourrais utiliser new List(Collection2.filter()) bien sûr, mais de cette façon, il n'est pas garanti que ma commande reste la même.

Ce n'est pas vrai. Collections2.filter() est une fonction évaluée paresseusement - elle ne filtre pas réellement votre collection jusqu'à ce que vous commenciez à accéder à la version filtrée. Par exemple, si vous parcourez la version filtrée, les éléments filtrés sortiront de l'itérateur dans le même ordre que votre collection d'origine (moins ceux filtrés, évidemment).

Peut-être que vous pensiez qu'il filtre à l'avance, puis transfère les résultats dans une collection arbitraire et non ordonnée d'une certaine forme - ce n'est pas le cas.

Donc, si vous utilisez la sortie de Collections2.filter() comme entrée dans une nouvelle liste, votre ordre d'origine sera sera conservé.

Utilisation d'importations statiques (et du Lists.newArrayList fonction), il devient assez succinct:

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

Notez que tandis que Collections2.filter n'itérera pas avec impatience la collection sous-jacente, Lists.newArrayListwill - il va extraire tous les éléments de la collection filtrée et les copier dans un nouveau ArrayList.

28
skaffman

Comme mentionné par Jon, vous pouvez utiliser Iterables.filter(..) ou Collections2.filter(..) et si vous n'avez pas besoin d'une vue en direct, vous pouvez utiliser ImmutableList.copyOf(Iterables.filter(..)) ou Lists.newArrayList( Iterables.filter(..)) et oui la commande sera maintenue.

Si vous êtes vraiment intéressé par pourquoi la partie, vous pouvez visiter https://github.com/google/guava/issues/505 pour plus de détails.

12
Premraj

Pour résumer ce que les autres ont dit, vous pouvez facilement créer un wrapper générique pour filtrer les listes:

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