abbiamo già iniziato a parlare di stream in riferimento ai file.
java.util.stream.Stream è un’interfaccia che rappresenta una sequenza di elementi su cui possono essere effettuate una o più operazioni.
supporta operazioni sequenziali e parallele
viene creato a partire da una sorgente di dati (come una java.util.Collection)
al contrario delle Collection, uno Stream non memorizza né modifica i dati della sorgente, ma opera su di essi
le operazioni possono essere:
intermedie - restituiscono un altro stream su cui continuare a lavorare
terminali - restituiscono il tipo atteso - una volta che uno stream è stato usato, non può essere riutilizzato
si segue un builder pattern: si impostano una serie di operazioni (intermedie) per configurare e infine si costruisce l’oggetto (operazione terminale)
le Stream hanno un comportamento pigro: le operazioni non vengono eseguite immediatamente, ma solo quando si richiede l’esecuzione di un’operazione terminale.
le operazioni possono essere:
stateless: l’elaborazione degli elementi può procedere in modo indipendente (non c’è ordine)
stateful: l’elaborazione di un elemento potrebbe dipendere da quella di altri elementi
IntStream, DoubleStream, LongStream
poiché Stream opera su oggetti, esistono analoghe versioni ottimizzate per lavorare con 3 tipi primitivi:
int (IntStream), double (DoubleStream), long (LongStream)
tutte queste estendono l’interfaccia di base BaseStream
si ottengono da uno stream con i metodi mapToInt, mapToLong, mapToDouble
dispongono di due metodi statici:
range(inizio, fine) - intervallo aperto a destra
rangeClosed(inizio, fine) - intervallo chiuso a destra
IntStream ottenuto da stream di array di interi
Arrays.stream(new int[]{1,2,3}) //restituisce un IntStream .map(n->2 * n+1) .average() .ifPresent(System.out::println);
ottenere uno stream
direttamente dai dati con il metodo statico generico
Stream.of(elenco di dati)
da Java8, dalle Collection:
default Stream<E> stream()
default Stream<E> parallelStream() - restituisce uno stream parallelo se possibile (altrimenti sequenziale)
da un array:
Stream<T> Arrays.stream(T[] array)
uno stream di testo da BufferedReader.lines()
FINISICI
stream vs collection
stream
collection
permette di usare uno stile dichiarativo - iterazione interna sui dati
impone l’utilizzo di uno stile imperativo - iterazione esterna sui dati (tranne che per ForEach)
impone l’utilizzo di uno stile imperativo - iterazione esterna sui dati (tranne che per ForEach)
metodi delle stream
java.util.stream.Stream<T>
min e max
restituiscono il minimo e il massimo di uno stream sotto forma di Optional
prendono in input un Comparator sul tipo degli elementi dello stream
filter, forEach
filter accetta un Predicate per filtrare gli elementi dello stream, ed è un’operazione intermedia che restituisce lo stream filtrato
forEach prende in input un Consumer e lo applica ad ogni elemento dello stream, ed è un’operazione terminale
esempi
filtra gli elementi di una lista di interi mantenendo solo quelli dispari e stampa ciascun elemento rimanente:
List< Integer> l = Arrays.asList(4,8,15,16,23,42)l.stream().filter(k-> k%2 = = 1).forEach(Sysyem.out::println);
count
operazione terminale che restituisce il numero long di elementi nello stream
sorted
operazione intermedia che restituisce una vista ordinata senza modificare la collezione sottostante
map
operazione intermedia sugli stream che restituisce un nuovo stream in cui ciascun elemento dello stream di origine è convertito in un altro oggetto attraverso la Function passata in input
partitioningBy(predicato) raggruppa in una Map<Boolean, List<T>>
crea una mappa da booleano a lista di interi che soddisfano quel criterio di predicato in input (quindi una mappa a “due” elementi: le cose che rispettano la condizione e quelle che non la rispettano)
distinct
restituisce un nuovo stream senza ripetizione di elementi (gli elementi sono tutti distinti tra loro)
quindi, per creare una lista con solo elementi distinti
operazione terminale che effettua una riduzione sugli elementi dello stream utilizzando la funzione data in input.
prende due input:
elemento di identità: l’ “accumulatore” su cui si reduce
la funzione con cui ridurre
//invece di fare:for(int k : lista) somma +=k;//failista.stream().reduce(0, (a,b)->a+b);//oppurelista.stream().reduce(0, Integer::sum);
versione con un parametro solo
esiste anche una versione di reduce con un solo parametro - senza elemento di identità - che restituisce un Optional<T> perché, se lo stream è vuoto, non avendo l’elemento identità non si sa cosa restituire
essenzialmente, “appiattisce” le collection (collection di collection → collection di elementi dalle collection)
Map<String, Long> letterToCount =words.map(w->w.split("")) //restituisce String[]//passi a flatmap il supplier che //rende la collection uno stream .flatMap(Arrays::stream) .collect(groupingBy(identity(), counting()));
con il metodo iterate() che, partendo dal primo argomento, restituisce uno stream infinito con valori successivi applicando la funzione passata come secondo argomento