variable validar que preguntar objeto igual diferente como java collections refactoring guava

java - validar - ¿Existe una manera elegante de eliminar los nulos mientras se transforma una Colección usando Guava?



variable igual a null en java (5)

Tengo una pregunta acerca de cómo simplificar algunos códigos de manejo de colecciones cuando utilizo Google Collections ( actualización : Guava ).

Tengo un montón de objetos de "Computadora", y quiero terminar con una Colección de sus "Identificación de recursos". Esto se hace así:

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

Ahora, getResourceId() puede devolver nulo (y cambiar eso no es una opción ahora), pero en este caso me gustaría omitir los nulos de la colección de cadenas resultante.

Aquí hay una forma de filtrar los nulos:

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

Podrías juntar todo eso así:

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; } });

¡Pero esto no es muy elegante, y mucho menos legible, para una tarea tan simple! De hecho, el simple código antiguo de Java (sin elementos de Predicado o Funcionalidad en absoluto) podría decirse que sería mucho más limpio:

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

Usar lo anterior también es una opción, pero por curiosidad (y deseo de aprender más sobre Google Collections), ¿ puede hacer exactamente lo mismo de una manera más corta o más elegante usando Google Collections ?


En primer lugar, crearía un filtro constante en alguna parte:

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

Entonces puedes usar:

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));

Puedes usar el mismo filtro nulo en todas partes en tu código.

Si usa la misma función de cálculo en otro lugar, también puede hacer que sea una constante, dejando simplemente:

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

Ciertamente no es tan agradable como sería el equivalente C #, pero todo esto va a ser mucho mejor en Java 7 con cierres y métodos de extensión :)


Podrías escribir tu propio método así. esto filtrará nulos para cualquier función que devuelva nulo del método 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()); }

El método puede llamarse con el siguiente código.

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);


Tomó más tiempo del esperado por @Jon Skeet , pero las secuencias de Java 8 lo hacen simple:

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

También puede usar .filter(x -> x != null) si lo desea; la diferencia es muy leve .


Una sintaxis un poco "más bonita" con FluentIterable (desde 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(); } };

Tenga en cuenta que la lista devuelta es una ImmutableList . Sin embargo, puede usar el método copyInto() para verter los elementos en una colección arbitraria.


Ya hay un predicado en Predicates que lo ayudará aquí - Predicates.notNull() - y puede usar Iterables.filter() y el hecho de que Lists.newArrayList() puede tomar un Iterable para limpiar esto un poco más.

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

Si realmente no necesita una Collection , solo una Iterable , entonces la llamada a Lists.newArrayList() puede desaparecer también y usted es un limpiador de pasos una vez más.

Sospecho que puede encontrar que la Function volverá a ser útil y será más útil declarada como

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

lo cual limpia aún más (y promoverá la reutilización).