KotlinEquivalenti Java 8 Stream


introduzione

Kotlin fornisce molti metodi di estensione su raccolte e iterabili per l'applicazione di operazioni in stile funzionale. Un tipo di Sequence dedicato consente la composizione lenta di diverse di tali operazioni.

Osservazioni

A proposito di pigrizia

Se si desidera elaborare una catena pigro, è possibile convertire in una Sequence utilizzando asSequence() prima della catena. Alla fine della catena di funzioni, di solito si ottiene anche una Sequence . Quindi puoi usare toList() , toSet() , toMap() o qualche altra funzione per materializzare la Sequence alla fine.

// switch to and from lazy
val someList = items.asSequence().filter { ... }.take(10).map { ... }.toList()

// switch to lazy, but sorted() brings us out again at the end
val someList = items.asSequence().filter { ... }.take(10).map { ... }.sorted()

Perché non ci sono tipi?!?

Noterai che gli esempi di Kotlin non specificano i tipi. Questo perché Kotlin ha l'inferenza di tipo completo ed è completamente sicuro al momento della compilazione. Più che Java perché ha anche i tipi nullable e può aiutare a prevenire il temuto NPE. Quindi questo in Kotlin:

val someList = people.filter { it.age <= 30 }.map { it.name }

equivale a:

val someList: List<String> = people.filter { it.age <= 30 }.map { it.name }

Poiché Kotlin sa cosa sono le people e che people.age è Int l'espressione di filtro consente solo il confronto con un Int e that people.name è una String pertanto il passo della map produce un List<String> ( List di String di sola lettura).

Ora, se le people fossero forse null , come in una List<People>? poi:

val someList = people?.filter { it.age <= 30 }?.map { it.name }

Restituisce un List<String>? che avrebbe bisogno di essere controllato con il nulla ( o usare uno degli altri operatori di Kotlin per valori nullable, vedi questo modo idiomatico di Kotlin per gestire valori nullable e anche il modo Idiomatic di gestire l'elenco nullable o vuoto in Kotlin )

Riutilizzo di flussi

In Kotlin, dipende dal tipo di raccolta se può essere consumata più di una volta. Una Sequence genera un nuovo iteratore ogni volta e, a meno che non asserisca "usa una sola volta", può reimpostarsi all'inizio ogni volta che viene eseguito. Pertanto, mentre il seguente non funziona nello stream di Java 8, ma funziona in Kotlin:

// Java:
Stream<String> stream =
Stream.of("d2", "a2", "b1", "b3", "c").filter(s -> s.startsWith("b"));

stream.anyMatch(s -> true);    // ok
stream.noneMatch(s -> true);   // exception
// Kotlin:  
val stream = listOf("d2", "a2", "b1", "b3", "c").asSequence().filter { it.startsWith('b' ) }

stream.forEach(::println) // b1, b2

println("Any B ${stream.any { it.startsWith('b') }}") // Any B true
println("Any C ${stream.any { it.startsWith('c') }}") // Any C false

stream.forEach(::println) // b1, b2

E in Java per ottenere lo stesso comportamento:

// Java:
Supplier<Stream<String>> streamSupplier =
    () -> Stream.of("d2", "a2", "b1", "b3", "c")
          .filter(s -> s.startsWith("a"));

streamSupplier.get().anyMatch(s -> true);   // ok
streamSupplier.get().noneMatch(s -> true);  // ok

Pertanto in Kotlin il fornitore dei dati decide se resettare e fornire un nuovo iteratore oppure no. Ma se vuoi vincolare intenzionalmente una Sequence ad una iterazione una sola volta, puoi usare la funzione constrainOnce() per Sequence come segue:

val stream = listOf("d2", "a2", "b1", "b3", "c").asSequence().filter { it.startsWith('b' ) }
        .constrainOnce()

stream.forEach(::println) // b1, b2
stream.forEach(::println) // Error:java.lang.IllegalStateException: This sequence can be consumed only once. 

Guarda anche:

Equivalenti Java 8 Stream Esempi correlati