Gibt es eine elegante Art und Weise nulls zu entfernen, während eine Sammlung mit Guava verwandeln?

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

Frage

Ich habe eine Frage zu etwas Sammlung Behandlungscode zu vereinfachen, bei der Verwendung von Google Sammlungen ( update : Guava ).

Ich habe eine Reihe von „Computer“ Objekte bekam, und ich möchte mit einer Sammlung ihrer „Ressource-ID“ s am Ende. Dies geschieht wie folgt:

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

Nun getResourceId() kann null zurück (und das Ändern ist keine Option jetzt), aber in diesem Fall würde Ich mag nulls aus der resultierenden String Sammlung verzichtet werden kann.

Hier ist eine Möglichkeit, NULL-Werte herauszufiltern:

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

Sie können alle setzen, die zusammen wie folgt:

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

Das ist aber kaum elegant, geschweige denn lesbar, für eine so einfache Aufgabe! In der Tat wäre wohl nur alter Java-Code (ohne Phantasie Prädikat oder Funktion Sachen überhaupt) viel sauberer sein:

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

Unter Verwendung der oben ist sicherlich auch eine Option, aber aus Neugier (und der Wunsch, mehr von Google Kollektionen zu lernen), können Sie tun genau das gleiche, was in einigen kürzeren oder eleganteren Weg mit Google Kollektionen ?

War es hilfreich?

Lösung

Es gibt bereits ein Prädikat in Predicates , die Ihnen hier helfen - Predicates.notNull() -. und Sie können Iterables.filter() und die Tatsache nutzen, dass Lists.newArrayList() eine Iterable dies ein wenig mehr

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

Wenn Sie nicht tatsächlich einen Collection benötigen, nur ein Iterable, dann wird der Lists.newArrayList() Anruf kann auch weggehen und Sie sind ein Schritt Reiniger wieder!

Ich vermute, Sie werden feststellen, dass die Function wieder nützlich sein, und wird am nützlichsten erklärt wie

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

, die dies noch bereinigt (und fördert die Wiederverwendung).

Andere Tipps

Ein bisschen "schönere" Syntax mit FluentIterable (seit 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();
        }
    };

Beachten Sie, dass die zurückgegebene Liste ein ImmutableList ist. Sie können jedoch copyInto() Verfahren zu gießen, die Elemente in einer beliebigen Sammlung verwenden.

Es dauerte länger als @ Jon Skeet erwartet, aber Java 8 Streams tun machen diese einfach:

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

Sie können auch .filter(x -> x != null) verwenden, wenn Sie mögen; der Unterschied sehr gering ist.

Zum einen würde ich einen konstanten Filter erstellen irgendwo:

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

Dann können Sie:

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

Sie können die gleichen Nullfilter überall in Ihrem Code verwenden.

Wenn Sie die gleiche Berechnungsfunktion an anderer Stelle verwenden, können Sie, dass eine Konstante machen, so dass nur:

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

Es ist sicherlich nicht so schön wie das C # -Äquivalent wäre, aber das alles wird ein Los schöner in Java 7 mit Schließungen und Erweiterungsmethoden bekommen:)

Sie können Ihre eigene Methode, wie so schreiben. dies wird nulls für jede Funktion herauszufiltern, die von der Anwendung Methode null zurück.

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

Das Verfahren kann dann mit dem folgenden Code aufgerufen werden.

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);
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top