Le operazioni possono essere terminali o intermedie
Intermedie: restituiscono un altro stream su cui continuare a lavorare
Terminali: restituiscono il tipo atteso
Una volta che uno stream è stato consumato (operazione terminale), non può essere riutilizzato
Builder pattern : si impostano una serie di operazioni per configurare e infine si costruisce l’oggetto
Metodi principali dell’interfaccia java.util.stream.Stream<T>
Operazioni intermedie e terminali
Una volta che uno stream è stato consumato, non può essere riutilizzato
Comportamento pigro: le operazioni intermedie non vengono eseguite immediatamente, ma solo quando si richiede l’esecuzione di un’operazione termianale
Le operazioni possono essere:
senza stato: l’elaborazione dei vari elementi può procedere in modo indipendente
con stato: l’elaborazione di un elemento potrebbe dipendere da quella di altri elementi
Come ottenere uno stream
Direttamente dai dati: con il metodo statico generico Stream.of(elenco di dati di un certo tipo)
In Java 8 l’interfaccia Collection è stata estesa per includere die nuovi metodi di default
default Stream <E>stream() restituisce un nuovo stream sequenziale
default Stream<E> parallelStream() restituisce un nuovo stream parallelo, se possibile (altrimenti restituisce uno stream sequenziale)
E’ possibile ottenere uno stream anche per un array, con il metodo statico Stream<T> Arrays.stream(T[ ] array)
E’ possibile ottenere uno stream di righe di testo da BufferedReader.lines() oppure da Files.lines(Path)
E’ possibile ottenere uno stream di righe anche da String.lines
Stream vs Collection
Lo stream permette di utilizzare uno stile dichiarativo
Iterazione interna sui dati
La collection impone l’utilizzo di uno stile imperativo
Iterazione esterna sui dati (tranne con forEach)
Lo stream si focalizza sulle operazioni di alto livello da eseguire (mattoncini o building blocks) eventualmente anche in parallelo, senza specificare come verranno eseguite
I metodi min e max restituiscono rispettivamente il minimo e il massimo di uno stream sotto forma di Optional
Prendono in input un Comparator sul tipo degli elementi dello stream
List<Integer> p = Arrays.asList(2, 3, 4, 5, 6, 7);Optional<Integer> max = p.stream().max(Integer::compare);//se c'è, restituisce il massimo, altrimenti -1System.out.println(max.orElse(-1));
Collect
E’ un’operazioe terminale che permette di raccogliere gli elementi dello stream in un qualche oggetto
Ad esempio, per ottenere la lista dei prezzi ivati:
List<Integer>ivaEsclusa = Arrays.asList(10, 20, 30); //java 7List<Double> I = new ArrayList<>();//java 8List<Double> I = ivaEsclusa.stream().map(p -> p*1.22) .collect(Collectors.toList());
Esempio: creare una stringa che concatena stringhe in un elenco, rese maiuscole e separate da virgola
List<String> l = Arrays.asList("RoMa", "milano", "Torino");String s = "";// in Java 7:for (String e : l) s += e.toUpperCase()+", ";s = s.substring(0, s.length()-2);// in Java 8:s = l.stream().map(e -> e.toUpperCase()).collect(Collectors.joining(", "));
Esempio: trasformare una lista di stringhe in una lista delle lunghezze delle stesse
List<String> words = Arrays.asList("Oracle", "Java","Magazine");List<Integer> wordLengths = words.stream() .map(String::length) .collect(toList());
Collectors
Ricettario che ci permette di ridurre gli elementi di uno stream per raccoglierli in qualche modo
Per rendere il codice piĂą leggibile: import static java.util.stream.Collectors
In questo modo possiamo scrivere il nome del metodo senza anteporre Collectors (ex. toList() invece di Collectors.toList())
Collectors: riduzioni a singolo elemento
counting() restituisce il numero di elementi nello stream (risultato di tipo long)
List<Integer> l = Arrays.asList(2, 3, 5, 6);// k == 2long k = l.stream().filter(x->x<5).collect(Collectors.counting()));
maxBy/minBy(comparator) restituisce un Optional con il massimo/minimo valore
// max contiene 6Optional<Integer> max = l.stream().collect(maxBy(Integer::compareTo));
summingInt(lambda che mappa ogni elemento a intero)/averagingInt, summingDouble, averagingDouble
Collectors.toMap: riduzione a una mappa
toMap prende input fino a 4 argomenti:
la funzione per mappare l’oggetto dello stream nella chiave della mappa
la funzione per mappare l’oggetto dello stream nel valore della mappa
opzionale: la funzione da utilizzare per unire il valore preesistente nella mappa a fronte della chiave con il valore associato all’oggetto dalla seconda funzione (non devono trovarsi due chiavi uguali o si ottiene un’eccezione IllegalStateException)