Czy istnieje elegancki sposób na usunięcie Nulli podczas przekształcania kolekcji za pomocą Guava?

Mam pytanie dotyczące uproszczenia kodu obsługi kolekcji przy użyciu Google Collections (update: guawa ).

W tym samym roku, w 2011 roku, w ramach projektu" resource id", w ramach projektu"resource id", w ramach projektu "resource id", w 2012 roku, w ramach projektu "resource id", w 2013 roku, w ramach projektu "resource id".]}
Collection<Computer> matchingComputers = findComputers();
Collection<String> resourceIds = 
    Lists.newArrayList(Iterables.transform(matchingComputers, new Function<Computer, String>() {
    public String apply(Computer from) {
        return from.getResourceId();
    }
}));

Teraz, getResourceId() może zwrócić null( i zmiana tego nie jest obecnie opcją), jednak w tym przypadku chciałbym pominąć null z wynikowej kolekcji łańcuchów.

Oto jeden ze sposobów filtrowania null out:

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

Można by to wszystko poskładać tak:

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

Ale to nie jest eleganckie, a co dopiero czytelne, dla tak prostego zadania! W rzeczywistości zwykły stary kod Javy (bez wymyślnych predykatów lub funkcji w ogóle) byłby prawdopodobnie dużo czystszy: {]}

Collection<String> resourceIds = Lists.newArrayList();
for (Computer computer : matchingComputers) {
    String resourceId = computer.getResourceId();
    if (resourceId != null) {
        resourceIds.add(resourceId);
    }
}
Korzystanie z powyższego z pewnością jest również opcją, ale z ciekawości (i chęci poznania więcej kolekcji Google), czy możesz zrobić dokładnie to samo w jakiś krótszy lub bardziej elegancki sposób za pomocą Google Kolekcje ?
Author: Jonik, 2009-11-26

5 answers

Jest już orzeczenie w Predicates to ci pomoże ... Predicates.notNull() ... i możesz użyć Iterables.filter() i faktu, że Lists.newArrayList() może zająć Iterable, aby posprzątać to trochę więcej.

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

Jeśli faktycznie nie potrzebujesz Collection, tylko Iterable, wtedy Lists.newArrayList() połączenie może odejść zbyt i jesteś o krok czystszy ponownie!

Podejrzewam, że może się okazać, że Function znów się przyda i będzie najbardziej przydatny jako

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

Który oczyszcza to jeszcze bardziej (i będzie promować ponowne użycie).

 78
Author: Cowan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-29 10:27:13

Nieco" ładniejsza " składnia z FluentIterable (od 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();
        }
    };

Zwróć uwagę, że zwracana lista jest ImmutableList. Można jednak użyć metody copyInto(), aby wlać elementy do dowolnego zbioru.

 35
Author: Natix,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-29 10:27:34

Trwało to dłużej niż @Jon Skeet oczekiwał , ale strumienie Java 8 robią to prosto:

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

Możesz również użyć .filter(x -> x != null) jeśli chcesz; różnica jest bardzo niewielka .

 13
Author: Jeffrey Bosboom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-07 03:00:50

Po pierwsze, stworzyłbym gdzieś stały filtr:

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

Wtedy możesz użyć:

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

Możesz użyć tego samego filtra null wszędzie w kodzie.

Jeśli używasz tej samej funkcji obliczeniowej gdzie indziej, możesz też uczynić ją stałą, pozostawiając tylko:

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

Na pewno nie jest tak miły jak odpowiednik C#, ale to wszystko będzie się lot ładniejszy w Javie 7 z zamknięciami i metodami rozszerzeń:)

 5
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-26 09:52:31

Możesz napisać własną metodę w ten sposób. to odfiltruje NULL dla dowolnej funkcji, która zwraca null z metody 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());
    }

Metoda może być wywołana następującym kodem.

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);
 1
Author: BewdyM8,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-25 05:08:29