wake-up-neo.net

Javas flatMap in der Liste der optionalen Ganzzahlen

Ich habe den folgenden vereinfachten Code, der nicht kompiliert werden kann, und ich kann nicht verstehen, warum:

List<Optional<Integer>> list =
    new ArrayList<>();
List<Integer> flattened = 
  list
    .stream()
    .flatMap(i -> i)
    .collect(Collectors.toList());

Der Compiler sagt mir:

[ERROR] ... incompatible types: cannot infer type-variable(s) R
[ERROR]     (argument mismatch; bad return type in lambda expression
[ERROR]       Optional<Integer> cannot be converted to Stream<? extends R>)
[ERROR]   where R,T are type-variables:
[ERROR]     R extends Object declared in method <R>flatMap(Function<? super T,? extends Stream<? extends R>>)
[ERROR]     T extends Object declared in interface Stream

Ich gebe zu, ich bin nicht an Java gewöhnt, aber ich muss für ein Projekt. Ich habe das in Scala verspottet, wo list.flatten und das entsprechende list.flatMap(i => i) genau wie erwartet funktionieren:

val list = List(Some(1), Some(2), None)
list.flatten // List(1, 2)

Ist Javas flatMap anders?

8
Max Power

Es sollte sein:

List<Integer> flattened = 
  list
    .stream()
    .filter (Optional::isPresent)
    .map(Optional::get)
    .collect(Collectors.toList());

Ihre flatMap erwartet eine Funktion, die ein Stream-Element in eine Stream umwandelt. Verwenden Sie stattdessen map (um den Wert der Optional zu extrahieren). Außerdem müssen Sie leere Optionals herausfiltern (es sei denn, Sie möchten sie in nulls umwandeln).

Ohne die Filterung:

List<Integer> flattened = 
  list
    .stream()
    .map(o -> o.orElse(null))
    .collect(Collectors.toList());
14
Eran

Dokumentation von Stream.flatMap(Function<? extends T, ? extends Stream<? extends R>>):

Gibt einen Stream zurück, der aus den Ergebnissen besteht, in denen jedes Element dieses Streams durch den Inhalt eines zugeordneten Streams ersetzt wird, der durch Anwenden der bereitgestellten Zuordnungsfunktion auf jedes Element erzeugt wird. Jeder zugeordnete Stream wird geschlossen, nachdem der Inhalt in diesen Stream eingefügt wurde. (Wenn ein zugeordneter Stream null ist, wird stattdessen ein leerer Stream verwendet.) Dies ist eine Zwischenoperation.

API-Hinweis:

Die flatMap()-Operation hat den Effekt, dass eine Eins-zu-Viele-Umwandlung auf die Elemente des Streams angewendet wird und die resultierenden Elemente dann zu einem neuen Stream reduziert werden.

Wie Sie sehen, wird die Methode verwendet, um jedes Element zu erstellen, aus denen ein stream erstellt wird, und dann jeden Stream in einen einzelnen Stream zu glätten (der von der Methode zurückgegeben wird). So zu tun:

List<Optional<Integer>> optionals = ...;
List<Integer> integers = optionals.stream()
        .flatMap(optional -> optional) // identity function
        .collect(Collectors.toList());

Funktioniert nicht, da die Funktion eine Optional und keine Stream von Integers zurückgibt. Um dieses Problem zu lösen, müssen Sie die Funktion ändern, um eine Stream des Inhalts der Optional zurückzugeben. Abhängig von der verwendeten Java-Version können Sie Folgendes tun:

Java 9+,

List<Optional<Integer>> optionals = ...;
List<Integer> integers = optionals.stream()
        .flatMap(Optional::stream)
        .collect(Collectors.toList());

Java 8,

// There are different ways to convert an Optional into a Stream using
// flatMap, this is just one option. Holger shows other ways in the comments.
List<Optional<Integer>> optionals = ...;
List<Integer> integers = optionals.stream()
        .flatMap(optional -> optional.isPresent() ? Stream.of(optional.get()) : Stream.empty())
        .collect(Collectors.toList());

Andere Optionen umfassen die Verwendung von map in Kombination mit filter oder Methoden von Optional. Siehe Erans Antwort für Beispiele.

2
Slaw

Ist Javas flatMap anders?

Die flatMap-Methode erwartet, dass die bereitgestellte Funktion einen Stream zurückgibt, aber i -> i liefert dies nicht. In JDK8 müssen Sie einen Stream aus dem Optional erstellen und dann reduzieren:

 list.stream()
     .flatMap(i -> i.isPresent() ? Stream.of(i.get()) : Stream.empty())
     .collect(Collectors.toList());

oder in JDK9, kann in getan werden als:

  list.stream()
      .flatMap(Optional::stream)
      .collect(Collectors.toList());
2
Aomine