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

StackOverflow https://stackoverflow.com/questions/1802629

Question

J'ai une question sur la simplification du code de traitement des collections lorsque vous utilisez Google Collections ( mise à jour : Goyave ).

J'ai un tas de "Ordinateur". objets, et je veux me retrouver avec une collection de leurs "identificateurs de ressources". 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 renvoyer null (et changer ce n'est pas une option pour le moment), mais dans ce cas, j'aimerais omettre les nulls de la collection String résultante.

Voici un moyen 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 pas très élégant, encore moins lisible, pour une tâche aussi simple! En fait, le vieux code Java (sans prédicat ni fonction) serait beaucoup plus propre:

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

Utiliser ce qui précède est certainement aussi une option, mais par curiosité (et 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 ?

Était-ce utile?

La solution

Il existe déjà un prédicat dans Prédicats qui vous aideront ici - Prédicats.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 réellement besoin d'un Collection , mais simplement d'un Iterable , l'appel Lists.newArrayList () peut également disparaître. et vous êtes à nouveau plus propre!

Je suppose que vous constaterez peut-être que la Fonction sera à nouveau utile et sera plus utile déclarée comme

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

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

Autres conseils

Un peu "plus joli" syntaxe avec FluentIterable (depuis Guava 12):

ImmutableList<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 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 @Jon Skeet prévu , mais les flux Java 8 simplifient les choses:

List<String> 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 .

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 null partout dans votre code.

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

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

Ce n’est certainement pas aussi beau que l’équivalent C #, mais tout cela va devenir plus agréable lot dans Java 7 avec des méthodes de fermeture et d’extension:)

Vous pouvez écrire votre propre méthode comme ceci. cela filtrera les valeurs NULL pour toute fonction renvoyant 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);
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top