59 votes

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

J'ai une question à propos de la simplification de certaines Collection de code de traitement, lors de l'utilisation de Google Collections (mise à jour: Goyave).

J'ai un tas de "Ordinateur" objets, et je veux terminer avec une Collection de leurs "id de ressource". C'est 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 la valeur null (et un changement qui n'est pas une option à l'heure actuelle), mais dans ce cas je voudrais d'omettre les valeurs null de la Chaîne résultante de la collection.

Voici un moyen de filtrer les valeurs null:

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

Vous pourriez mettre tout ça 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, encore moins lisible, pour une tâche simple! En fait, plain old Java code (avec pas de fantaisie, Prédicat ou de la Fonction des choses à tous) 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'aide de la ci-dessus est certainement aussi une option, mais de la curiosité (et le désir d'apprendre plus de Google Collections), pouvez-vous faire exactement la même chose dans certains plus courte ou plus de manière élégante à l'aide de Google Collections?

81voto

Cowan Points 17235

Il y a déjà un prédicat dans les Prédicats qui va vous aider ici -- Prédicats.notNull() -- et vous pouvez utiliser Iterables.filter() et le fait que les Listes.newArrayList() peut prendre un objet iterable pour nettoyer cette place un peu plus.

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

Si vous n'avez pas réellement besoin d'un Collection, juste un objet iterable, puis les Listes.newArrayList() l'appel peut aller trop loin et vous avez fait un pas de plus propre à nouveau!

Je soupçonne que vous pourriez trouver que la Fonction va venir dans maniable à nouveau, et sera plus utile a déclaré que

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

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

35voto

Natix Points 4421

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

 List<String> resourceIds = FluentIterable.from(matchingComputers)
    .transform(getResourceId)
    .filter(Predicates.notNull())
    .toList();

static final Function<Computer, String> getResourceId =
    new Function<Computer, String>() {
        @Override
        public String apply(Computer computer) {
            return computer.getResourceId();
        }
    };
 

Notez que la liste renvoyée est un ImmutableList .

5voto

Jon Skeet Points 692016

Tout d'abord, je voudrais créer une constante de filtre 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 null partout dans votre code.

Si vous utilisez le même calcul de la fonction d'ailleurs, vous pouvez en faire une constante de trop, en laissant un peu:

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

Ce n'est certainement pas aussi beau que le C# équivalente, mais c'est tout va devenir beaucoup plus agréable dans Java 7 avec des fermetures et des méthodes d'extension :)

1voto

BewdyM8 Points 11

Vous pouvez écrire votre propre méthode comme ça. cela filtrera les valeurs NULL pour toute fonction qui renvoie 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);
 

-3voto

David Roussel Points 2019

Ce n'est pas chic, mais

 resourceIds.remove(null);
 

supprimera toutes les valeurs nulles de la collection. Et c'est probablement beaucoup plus rapide que d'utiliser des prédicats d'autres constructions de niveau supérieur.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X