2015-09-11 13 views
12

mi aspettavo di essere in grado di utilizzare flusso :: flatMap come questoPerché questo uso di Stream :: flatMap è sbagliato?

public static List<String> duplicate(String s) { 

    List<String> l = new ArrayList<String>(); 
    l.add(s); 
    l.add(s); 

    return l; 
} 


listOfStrings.stream().flatMap(str -> duplicate(str)).collect(Collectors.toList()); 

ma ottengo il seguente errore del compilatore

Test.java:25: error: incompatible types: cannot infer type-variable(s) R listOfStrings.stream().flatMap(str -> duplicate(str)).collect(Collectors.toList());

(argument mismatch; bad return type in lambda expression List cannot be converted to Stream)
where R,T are type-variables: R extends Object declared in method flatMap(Function>) T extends Object declared in interface Stream

a Scala che posso fare quello che io credo di essere equivalente

scala> List(1,2,3).flatMap(duplicate(_)) 
res0: List[Int] = List(1, 1, 2, 2, 3, 3) 

Perché questo non è un utilizzo valido di flatMap in java?

risposta

19

L'espressione lambda in flatMap deve restituire un Stream, come si può vedere con l'argomento di flatMap che è di tipo Function<? super T, ? extends Stream<? extends R>>.

Il seguente codice compilare ed eseguire bene:

listOfStrings.stream() 
      .flatMap(str -> duplicate(str).stream()) // note the .stream() here 
      .collect(Collectors.toList()); 

perché l'espressione lambda str -> duplicate(str).stream() è di tipo Function<String, Stream<String>>.

6

Se si desidera duplicare ogni oggetto nello stream più volte, non è necessario sprecare memoria su questo con ArrayList aggiuntivo. Esistono diverse alternative più brevi e veloci.

  • generare nuovo flusso utilizzando Stream.generate, quindi limitarla:

    listOfStrings.stream() 
          .flatMap(str -> Stream.generate(() -> str).limit(2)) 
          .collect(Collectors.toList()); 
    
  • Genera sequenza di numeri tramite IntStream.range e mappare la stessa stringa:

    listOfStrings.stream() 
          .flatMap(str -> IntStream.range(0, 2).mapToObj(i -> str)) 
          .collect(Collectors.toList()); 
    
  • Usa buon vecchio Collections.nCopies:

    listOfStrings.stream() 
          .flatMap(str -> Collections.nCopies(2, str).stream()) 
          .collect(Collectors.toList()); 
    

Se si è certi che sarete sempre duplicare esattamente due volte, c'è l'alternativa più breve:

listOfStrings.stream() 
      .flatMap(str -> Stream.of(str, str)) 
      .collect(Collectors.toList());