Existe-t-il un moyen élégant d’éliminer les valeurs nulles lors de la transformation d’une collection à l’aide de Guava?

J’ai une question à propos de la simplification de certains codes de gestion des collections, lors de l’utilisation de Google Collections ( mise à jour : Guava ).

J’ai un tas d’objects “Computer”, et je veux finir avec une collection de leurs “identifiant de ressource”. Ceci est fait comme ça:

Collection matchingComputers = findComputers(); Collection resourceIds = Lists.newArrayList(Iterables.transform(matchingComputers, new Function() { public Ssortingng apply(Computer from) { return from.getResourceId(); } })); 

Maintenant, getResourceId() peut renvoyer null (et changer cela n’est pas une option pour le moment), mais dans ce cas, je voudrais omettre les null de la collection de chaînes résultante.

Voici un moyen de filtrer les valeurs nulles:

 Collections2.filter(resourceIds, new Predicate() { @Override public boolean apply(Ssortingng input) { return input != null; } }); 

Vous pourriez mettre tout ça ensemble comme ceci:

 Collection resourceIds = Collections2.filter( Lists.newArrayList(Iterables.transform(matchingComputers, new Function() { public Ssortingng apply(Computer from) { return from.getResourceId(); } })), new Predicate() { @Override public boolean apply(Ssortingng input) { return input != null; } }); 

Mais ce n’est guère élégant, encore moins lisible, pour une tâche si simple! En fait, un vieux code Java simple (sans trucs de prédicats ou de fonctions) serait sans doute beaucoup plus propre:

 Collection resourceIds = Lists.newArrayList(); for (Computer computer : matchingComputers) { Ssortingng resourceId = computer.getResourceId(); if (resourceId != null) { resourceIds.add(resourceId); } } 

L’utilisation de ce qui précède est certainement également une option, mais par curiosité (et désir d’en savoir plus sur Google Collections), pouvez-vous faire exactement la même chose de manière plus courte ou plus élégante en utilisant Google Collections ?

Il existe déjà un prédicat dans Predicates qui vous aidera ici – Predicates.notNull() – et vous pouvez utiliser Iterables.filter() et le fait que Lists.newArrayList() peut prendre une Iterable pour le nettoyer un peu plus.

 Collection resourceIds = Lists.newArrayList( Iterables.filter( Iterables.transform(matchingComputers, yourFunction), Predicates.notNull() ) ); 

Si vous n’avez pas vraiment besoin d’une Collection , juste une Iterable , l’appel à Lists.newArrayList() peut également disparaître et vous êtes encore plus propre!

Je soupçonne que vous pourriez trouver que la Function sera utile à nouveau, et sera très utile déclaré comme

 public class Computer { // ... public static Function TO_ID = ...; } 

ce qui nettoie encore plus (et favorisera la réutilisation).

Une syntaxe un peu plus “jolie” avec FluentIterable (depuis Guava 12):

 ImmutableList resourceIds = FluentIterable.from(matchingComputers) .transform(getResourceId) .filter(Predicates.notNull()) .toList(); static final Function getResourceId = new Function() { @Override public Ssortingng apply(Computer computer) { return computer.getResourceId(); } }; 

Notez que la liste renvoyée est une ImmutableList . Cependant, vous pouvez utiliser la méthode copyInto() pour verser les éléments dans une collection arbitraire.

Cela a pris plus de temps que prévu avec @Jon Skeet , mais les stream Java 8 rendent cela simple:

 List resourceIds = computers.stream() .map(Computer::getResourceId) .filter(Objects::nonNull) .collect(Collectors.toList()); 

Vous pouvez également utiliser .filter(x -> x != null) si vous le souhaitez; la différence est très mineure .

Premièrement, je créerais un filtre constant quelque part:

 public static final Predicate NULL_FILTER = new Predicate() { @Override public boolean apply(Object input) { return input != null; } } 

Ensuite, vous pouvez utiliser:

 Iterable ids = Iterables.transform(matchingComputers, new Function() { public Ssortingng apply(Computer from) { return from.getResourceId(); } })); Collection resourceIds = Lists.newArrayList( Iterables.filter(ids, NULL_FILTER)); 

Vous pouvez utiliser le même filtre NULL partout dans votre code.

Si vous utilisez la même fonction informatique ailleurs, vous pouvez également en faire une constante, en ne laissant que:

 Collection resourceIds = Lists.newArrayList( Iterables.filter( Iterables.transform(matchingComputers, RESOURCE_ID_PROJECTION), NULL_FILTER)); 

Ce n’est certainement pas aussi beau que l’équivalent en C #, mais tout cela va devenir beaucoup plus agréable avec Java 7 avec les méthodes de fermeture et d’extension 🙂

Vous pourriez écrire votre propre méthode comme ça. Cela filtrera les valeurs null pour toute fonction renvoyant null à partir de la méthode apply.

  public static  Collection transformAndFilterNulls(List fromList, Function function) { return Collections2.filter(Lists.transform(fromList, function), Predicates.notNull()); } 

La méthode peut ensuite être appelée avec le code suivant.

 Collection c = transformAndFilterNulls(Lists.newArrayList("", "SD", "DDF"), new Function() { @Override public Long apply(Ssortingng s) { return s.isEmpty() ? 20L : null; } }); System.err.println(c);