Pregunta

Tengo una pregunta sobre la simplificación de algunos códigos de manejo de la Colección, cuando uso Google Collections ( update : Guava ).

Tengo un montón de " Computadora " objetos, y quiero terminar con una Colección de sus " ID de recursos " s. 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 en este momento), pero en este caso me gustaría omitir nulos en la colección de cadenas resultante.

Aquí hay una forma de filtrar nulos:

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

Podrías juntar todo eso de esta manera:

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 elegante, por no decir legible, para una tarea tan simple! De hecho, podría decirse que el código Java antiguo (sin ningún elemento sofisticado de Predicado o Función) 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);
    }
}

El uso de lo anterior es ciertamente también una opción, pero por curiosidad (y el deseo de aprender más sobre las Colecciones de Google), puede hacer exactamente lo mismo de una manera más breve o elegante usando las Colecciones de Google ?

¿Fue útil?

Solución

Ya hay un predicado en Predicates que te ayudarán aquí - Predicates.notNull () - y puedes 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 en realidad no necesita una Collection , solo una Iterable , la llamada Lists.newArrayList () también puede desaparecer ¡Y estás un paso más limpio otra vez!

Sospecho que podría encontrar que la Función volverá a ser útil, y será más útil declarada como

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

que limpia esto aún más (y promoverá la reutilización).

Otros consejos

Un poco " más bonito " sintaxis 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 un ImmutableList . Sin embargo, puede utilizar el método copyInto () para verter los elementos en una colección arbitraria.

Tardó más de lo que se esperaba @Jon Skeet , pero las secuencias de Java 8 hacen esto 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 pequeña .

Primero, 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 todo el código.

Si usa la misma función informática en otro lugar, también puede hacer que sea una constante, dejando solo:

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

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

Podrías escribir tu propio método como tal. Esto filtrará los valores nulos para cualquier función que devuelva un valor nulo del método de aplicación.

   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 se puede llamar 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);
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top