webentwicklung-frage-antwort-db.com.de

Verwendung der Java 8-Stream-API oder Else

Ich versuche zu tun, die Liste zu filtern, sie zuzuordnen und orElse wenn null zu verwenden, um sie dann wieder in die Liste aufzunehmen. Jetzt kann ich es so erreichen:

return users.stream()
    .filter(user -> id.equals(user.getId()))
    .map(
        user -> {
            if(user.getData() != null) {
                return user.getData();
            }
            return Collections.emptyMap();
        }
    )
    .collect(Collectors.toList());

Aber die Frage ist: Wie kann ich diese Struktur verbessern und warum kann ich in diesem Fall nicht orElse verwenden?

16

Mit einem ternären bedingten Operator könnte es besser lesbar sein:

return users.stream()
    .filter(user -> id.equals(user.getId()))
    .map(
        user -> (user.getData() != null) 
        ? user.getData() 
        : emptyMap()
    )
    .collect(Collectors.toList())
;

Um orElse verwenden zu können, müssen Sie eine Optional erstellen, die user.getData() umschließt. Ich bin mir nicht sicher, ob das eine gute Idee ist.

Wenn Sie darauf bestehen, orElse zu verwenden (oder noch besser: orElseGet, um zu vermeiden, dass emptyMap() ausgewertet wird, wenn dies nicht erforderlich ist), kann dies folgendermaßen aussehen:

return users.stream()
    .filter(user -> id.equals(user.getId()))
    .map(
        user -> Optional.ofNullable(
            user.getData()
        ).orElseGet(
            () -> emptyMap()
        )
    )
    .collect(Collectors.toList())
;
14
Eran

Wie ich auch in den Kommentaren darauf hingewiesen habe und bezweifle ich stark, dass Sie vielleicht nur das Folgende suchen

users
    .stream()
    .filter(
        user -> id.equals(user.getId()) 
        && (user.getData() != null)
    )
    .map(User::getData)
    .collect(Collectors.toList())
;

Aber dann ist die Frage nicht klar genug, um zu sagen, was der eventuelle Rückgabetyp Ihrer Anweisung ist oder welche emptyMap in Ihrem Code verwendet wird. Daher bezweifle ich stark, ob Sie für diesen Vorgang an erster Stelle eine Optional-API benötigen.

Hinweis: Bei der oben genannten Lösung wird davon ausgegangen, dass emptyMapCollections.emptyMap ist. Ich bin mir nicht sicher, warum man sie in einer Datenstruktur sammeln möchte, die als List<Map<K,V>> bezeichnet wird.

4
nullpointer

Wie kann ich diese Struktur verbessern? 

Methode 1:

return users.stream()
    .filter(user -> id.equals(user.getId()))
    .map(
        user -> (user.getData() != null)
        ? user.getData() 
        : emptyMap()
    )
    .collect(Collectors.toList())
;

Methode 2:

Machen Sie aus Ihrer getData eine Optional-Rückgabe: user -> user.getData().orElse(emptyMap())

Methode 3:

Als @Eran sagte: Optional.ofNullable dann orElse(emptyMap()) wie oben: user -> Optional.ofNullable(user.getData()).orElse(emptyMap())

Warum kann ich orElse in diesem Fall nicht verwenden?

Sie sind sich nicht sicher, was orElse Sie meinen

  1. Wenn user.getData()null zurückgibt, sollte es in ein Optional eingeschlossen werden, um orElse aufzurufen.

  2. Die findAny().orElse des Streams wirkt auf das Ergebnis des Streams selbst ein. Hier müssen Sie jedoch überprüfen, ob user.getData() vorhanden ist. Sie können orElse des Ergebnisses des Streams also nicht direkt verwenden.

1
shawn

Verwenden Sie Objects::requireNonNullElse !

Ich würde von zwei Dingen raten, um den Code lesbarer zu machen. Ich würde jedoch nicht künstlich eine Optional einführen.


Erste Option: Objects::requireNonNullElse in einer separaten Methode

List<Map<?, ?> bar() {
    //...

    return users.stream()
                .filter(user -> id.equals(user.getId()))
                .map(User::getData)
                .map(Foo::nullSafeMap)
                .collect(Collectors.toList());
}

private static Map<?, ?> nullSafeMap(final Map<?, ?> map) {
    return Objects.requireNonNullElse(map, Collections.emptyMap());
}

Hier würden Sie Objects::requireNonNullElse verwenden, der das im ersten Parameter übergebene Objekt zurückgibt, wenn es nicht null ist, und das Objekt als zweiten Parameter, wenn der erste Parameter null ist. Wenn eine separate Methode vorhanden ist, kann eine Methodenreferenz an Stream::map übergeben werden. Sie müssen jedoch zunächst die User-Instanzen ihren Daten Map zuordnen. 


Zweite Option: Inline Objects::requireNonNullElse

List<Map<?, ?> bar() {
    //...

    return users.stream()
                .filter(user -> id.equals(user.getId()))
                .map(User::getData)
                .map(map -> Objects.requireNonNullElse(map, Collections.emptyMap()))
                .collect(Collectors.toList());
}

Wenn Sie nicht möchten, dass eine separate Methode nur diese einzige Aufgabe ausführt, können Sie die Methode inline hinzufügen und optional sogar das erste Mapping zugunsten von .map(user -> Objects.requireNonNullElse(user.getData(), Collections.emptyMap())) entfernen. Ich würde jedoch davon abraten. Haben Sie keine Angst davor, mehrere Aufrufe von Stream::map zu erhalten, wenn der Code dadurch lesbarer wird.


Fazit

Ich würde die erste Option bevorzugen, da der Code dadurch sehr lesbar wird: Sie wissen, dass Sie die User-Instanzen den Daten zuordnen, und dann machen Sie diese Daten für null sicher. 

Die zweite Option ist in Ordnung, leidet jedoch an einer sehr langen Zeile, die auf den ersten Blick verwirrend sein kann. Es ist jedoch viel besser als ein mehrzeiliges Lambda. Ich würde mehrzeilige Lambdas um jeden Preis vermeiden und ihren Inhalt immer in einer separaten Methode extrahieren.

Möglicherweise können Sie den Methodennamen nullSafeMap verbessern, um Verwechslungen zwischen Stream::map und Java.util.Map zu vermeiden. 

Beachten Sie, dass Sie Objects::requireNonNullElseGet nicht verwenden müssen, da Collections::emptyMap eine einfache Methode ist, die nur eine Konstante konvertiert und zurückgibt:

public static final <K,V> Map<K,V> emptyMap() {
    return (Map<K,V>) EMPTY_MAP;
}

Objects::requireNonNullElseGet ist für Standardobjekte gedacht, deren Abruf oder Erstellung stark ist.

1
Marv

Wenn Sie bereits Apache Collections 4 als Abhängigkeit haben:

return users
    .stream()
    .filter(user -> id.equals(user.getId()))
    .map(User::getData)
    .map(MapUtils::emptyIfNull)
    .collect(Collectors.toList())
;

Wenn Sie Apache Collections nicht verwenden, definieren Sie einfach eine Hilfsmethode:

public static <K,V> Map<K,V> emptyIfNull(Map<K,V> map) {
    return map == null ? Collections.<K,V>emptyMap() : map;
}
0
Illya Kysil