web-dev-qa-db-fra.com

Existe-t-il un moyen élégant de supprimer les valeurs nulles lors de la transformation d'une collection à l'aide de Goyave?

J'ai une question sur la simplification du code de gestion des collections, lors de l'utilisation de Google Collections ( update: Guava ).

J'ai un tas d'objets "Computer", et je veux me retrouver avec une collection de leurs "id de ressource". Cela se fait comme suit:

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

Maintenant, getResourceId() peut retourner null (et changer ce n'est pas une option pour le moment), mais dans ce cas, j'aimerais omettre les null de la collection String résultante.

Voici une façon de filtrer les valeurs nulles:

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

Vous pouvez mettre tout cela ensemble comme ceci:

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

Mais ce n'est guère élégant, et encore moins lisible, pour une tâche aussi simple! En fait, un vieux Java (sans aucun prédicat ou fonction sophistiqué) serait sans doute beaucoup plus propre:

Collection<String> resourceIds = Lists.newArrayList();
for (Computer computer : matchingComputers) {
    String 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 d'une manière plus courte ou plus élégante en utilisant Google Collections ?

59
Jonik

Il y a 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 un Iterable pour nettoyer cela un peu plus.

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

Si vous n'avez pas vraiment besoin d'un Collection, juste d'un Iterable, alors l'appel de Lists.newArrayList() peut disparaître aussi et vous êtes encore une fois plus propre!

Je soupçonne que vous trouverez peut-être que le Function sera à nouveau utile et sera le plus utile déclaré comme

public class Computer {
    // ...
    public static Function<Computer, String> TO_ID = ...;
}

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

79
Cowan

Tout d'abord, je créerais un filtre constant quelque part:

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

Ensuite, vous pouvez utiliser:

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

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

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

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

Ce n'est certainement pas aussi agréable que l'équivalent C #, mais tout cela va obtenir un lot plus agréable en Java 7 avec fermetures et méthodes d'extension :)

5
Jon Skeet

Vous pouvez écrire votre propre méthode comme ça. cela filtrera les valeurs nulles pour toute fonction qui renvoie la valeur null à partir de la méthode apply.

   public static <F, T> Collection<T> transformAndFilterNulls(List<F> fromList, Function<? super F, ? extends T> function) {
        return Collections2.filter(Lists.transform(fromList, function), Predicates.<T>notNull());
    }

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

Collection c = transformAndFilterNulls(Lists.newArrayList("", "SD", "DDF"), new Function<String, Long>() {

    @Override
    public Long apply(String s) {
        return s.isEmpty() ? 20L : null;
    }
});
System.err.println(c);
1
BewdyM8