Domanda

Ho una domanda sulla semplificazione del codice di gestione delle raccolte quando utilizzo Google Collections ( aggiorna : Guava ).

Ho un sacco di " Computer " oggetti e voglio finire con una raccolta dei loro "ID risorsa". Questo è fatto in questo modo:

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

Ora, getResourceId () potrebbe restituire null (e cambiando che al momento non è un'opzione), ma in questo caso vorrei omettere i null dalla raccolta String risultante.

Ecco un modo per filtrare i null:

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

Potresti mettere tutto insieme così:

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

Ma questo non è certo elegante, figuriamoci leggibile, per un compito così semplice! In effetti, il semplice vecchio codice Java (senza alcun elaborato predicato o funzione) sarebbe probabilmente molto più pulito:

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

L'uso di quanto sopra è certamente anche un'opzione, ma per curiosità (e desiderio di saperne di più su Google Collections), puoi fare esattamente la stessa cosa in un modo più breve o più elegante usando Google Collections ?

È stato utile?

Soluzione

Esiste già un predicato in Predicati che ti aiuteranno qui - Predicates.notNull () - e puoi usare Iterables.filter () e il fatto che Lists.newArrayList () può richiedere un Iterable per ripulire un po 'di più.

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

Se in realtà non hai bisogno di un Collection , solo un Iterable , anche la chiamata Lists.newArrayList () può sparire e sei di nuovo più pulito!

Sospetto che potresti scoprire che la Funzione tornerà utile e che sarà molto utile dichiarata come

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

che risolve ulteriormente questo problema (e promuoverà il riutilizzo).

Altri suggerimenti

Un po '"più bello" sintassi con FluentIterable (da 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();
        }
    };

Si noti che l'elenco restituito è un ImmutableList . Tuttavia, puoi usare il metodo copyInto () per versare gli elementi in una raccolta arbitraria.

Ci è voluto più tempo di @Jon Skeet previsto , ma i flussi Java 8 lo rendono semplice:

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

Puoi anche usare .filter (x - > x! = null) se vuoi; la differenza è molto minore .

In primo luogo, creerei un filtro costante da qualche parte:

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

Quindi puoi usare:

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

Puoi usare lo stesso filtro null ovunque nel tuo codice.

Se usi la stessa funzione di calcolo altrove, puoi renderla costante, lasciando solo:

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

Non è certo bello come l'equivalente in C #, ma tutto ciò diventerà lotto più bello in Java 7 con chiusure e metodi di estensione :)

Potresti scrivere il tuo metodo in questo modo. questo filtrerà i null per qualsiasi Funzione che restituisce null dal metodo 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());
    }

Il metodo può quindi essere chiamato con il seguente codice.

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);
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top