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?
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())
;
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 emptyMap
Collections.emptyMap
ist. Ich bin mir nicht sicher, warum man sie in einer Datenstruktur sammeln möchte, die als List<Map<K,V>>
bezeichnet wird.
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
Wenn user.getData()
null
zurückgibt, sollte es in ein Optional
eingeschlossen werden, um orElse
aufzurufen.
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.
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.
Objects::requireNonNullElse
in einer separaten MethodeList<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.
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.
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.
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;
}