API Stream in Java
Esistono diversi modi per elaborare i dati in Java: cicli, metodi e vari algoritmi. Tuttavia, con Java 8 è stato introdotto uno strumento molto potente: la Stream API.
In termini semplici, la Stream API è un modo per lavorare in modo rapido e semplice con un flusso di informazioni. In questo caso, il flusso di informazioni è rappresentato dalle collezioni. La Stream API introduce alcuni concetti. Ecco i principali.
Concetti principali
-
Stream: rappresenta una sequenza di elementi di dati che possono essere elaborati;
-
Operazioni intermedie: operazioni che creano un nuovo stream dopo la loro esecuzione. Esempi:
filter,map,distinct,sorted; -
Operazioni terminali: operazioni che completano l'elaborazione dello stream e restituiscono un risultato. Esempi:
collect,forEach,count,reduce; -
Stream paralleli: consentono l'elaborazione parallela dei dati. I metodi
parallel()eparallelStream()vengono utilizzati per creare stream paralleli.
Basta parlare di teoria, iniziamo a programmare!
La dichiarazione di uno stream si effettua utilizzando un metodo sulla collezione che si desidera trasformare in uno stream:
Main.java
12List<String> strings = Arrays.asList("a", "b", "c"); Stream<String> stream = strings.stream();
Con il metodo stream(), si ottiene uno stream di stringhe. Tuttavia, per iniziare a lavorare con lo stream, è necessario comprendere cosa sono le espressioni lambda, poiché i metodi degli stream funzionano principalmente con esse.
Espressioni Lambda
Le espressioni lambda sono state introdotte in Java 8 e rappresentano una forma semplificata per creare funzioni anonime in Java. Non abbiamo trattato prima le funzioni anonime poiché non erano particolarmente necessarie, ma ora le conosceremo attraverso le espressioni lambda.
Sintassi delle Espressioni Lambda:
La sintassi generale per le espressioni lambda in Java è la seguente:
Example.java
123(parameters) -> expression // or (parameters) -> { statements; }
-
Parametri: elenco di parametri che può essere vuoto oppure contenere uno o più parametri;
-
Freccia: rappresentata dal simbolo
->, che separa i parametri dal corpo dell'espressione lambda; -
Espressione o istruzioni: corpo della funzione, che contiene un'espressione o un blocco di istruzioni.
Ecco un esempio di espressione lambda che rappresenta una funzione semplice che somma due numeri:
Example.java
12345678910// Traditional way MathOperation addition = new MathOperation() { @Override public int operate(int a, int b) { return a + b; } }; // Using a lambda expression MathOperation addition = (int a, int b) -> a + b;
Analizziamo più da vicino cosa succede esattamente nel codice sopra e come utilizziamo le espressioni lambda:
Main.java
1234567891011121314package com.example; // Functional interface with a single abstract method interface MyMathOperation { int operate(int a, int b); } public class Main { public static void main(String[] args) { // Using a lambda expression to implement the interface MyMathOperation addition = (a, b) -> a + b; System.out.println("Sum: " + addition.operate(5, 3)); } }
Nel codice sopra:
È stata creata un'interfaccia funzionale MyMathOperation con un singolo metodo astratto operate.
È stata utilizzata un'espressione lambda per implementare questo metodo, eseguendo la somma di due numeri.
È stato stampato il risultato della somma.
Si comprende che potrebbe essere difficile da capire cosa stia accadendo in questo codice per ora, ma torniamo alla Stream API, dove le espressioni lambda sono frequentemente utilizzate, e cerchiamo di capire come usarle nella pratica.
Come ricordato in precedenza, è stato creato uno stream di stringhe da una lista di stringhe. Ora, utilizziamo i metodi dello stream per rendere ogni stringa di questo stream maiuscola:
Main.java
12345678910111213package com.example; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("a", "b", "c"); Stream<String> stream = strings.stream(); stream.map(e -> e.toUpperCase()).toList(); } }
Nel codice sopra, è stata utilizzata un'espressione lambda e due metodi: map() e toList(). Se il funzionamento del metodo toList() è chiaro, il metodo map() modifica ogni elemento nello stream secondo la lambda expression fornita.
Analizziamo più da vicino come funziona l'espressione lambda in questo contesto:
Il metodo map() applica il metodo toUpperCase() a ciascun elemento dello stream. L'elemento di questo stream è stato definito come e e, utilizzando la lambda expression, si è indicato al programma di applicare questo metodo a ogni elemento.
Tuttavia, questa è solo un'operazione intermedia. Ciò significa che le operazioni sullo stream non sono ancora concluse. Per completare il lavoro sullo stream, è necessario applicare un'operazione terminale, che terminerà le operazioni sullo stream e restituirà un valore specifico. Ad esempio, è possibile utilizzare il metodo toList(), e lo stream modificato verrà convertito in una lista.
Ad esempio:
Main.java
1234567891011121314package com.example; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("a", "b", "c"); Stream<String> stream = strings.stream(); List<String> list = stream.map(e -> e.toUpperCase()).toList(); System.out.println(list); } }
Esaminiamo più da vicino le possibili operazioni intermedie nello stream.
Operazioni Intermedie
Il metodo map() - questo metodo è già noto; esegue le operazioni specificate dall'espressione lambda su ogni elemento dello stream.
Ad esempio, utilizziamo il metodo substring() su ciascun elemento nello stream di stringhe:
Main.java
123456789101112131415package com.example; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("Unlock", "Infinity", "with", "Codefinity"); System.out.println("List of strings: " + strings); Stream<String> stream = strings.stream(); List<String> list = stream.map(e -> e.substring(1, 4)).toList(); System.out.println("Modified list: " + list); } }
Il metodo filter() accetta un espressione lambda con una condizione in base alla quale lo stream verrà filtrato. In altre parole, tutti gli elementi che soddisfano la condizione rimarranno nello stream, mentre gli elementi che non soddisfano la condizione verranno rimossi dallo stream. Modifichiamo lo stream per mantenere solo gli elementi la cui lunghezza è maggiore di 5:
Main.java
12345678910111213141516package com.example; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("Unlock", "Infinity", "with", "Codefinity"); System.out.println("List of strings: " + strings); Stream<String> stream = strings.stream(); stream = stream.filter(e -> e.length() > 5); List<String> list = stream.map(e -> e.substring(1, 4)).toList(); System.out.println("Modified list: " + list); } }
Utilizzando il metodo filter(), la stringa "with" viene rimossa dallo stream perché questa parola contiene meno di 5 caratteri.
È inoltre possibile utilizzare operazioni intermedie più volte di seguito.
Ad esempio, è possibile semplificare leggermente il codice sopra riportato:
Main.java
12345678910111213141516package com.example; import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("Unlock", "Infinity", "with", "Codefinity"); System.out.println("List of strings: " + strings); List<String> list = strings.stream() .filter(e -> e.length() > 5) .map(e -> e.substring(1, 4)) .toList(); System.out.println("Modified list: " + list); } }
Quando si concatenano più metodi stream insieme, si consiglia di posizionare ciascun metodo su una nuova riga per migliorare significativamente la leggibilità del codice.
Il metodo flatMap() trasforma ogni elemento di uno stream in un nuovo stream e combina i risultati in un unico stream. In altre parole, con questo metodo, è possibile suddividere lo stream in più stream, che poi verranno uniti in un unico stream. Ad esempio, si dispone di una lista di stringhe in cui ciascuna stringa può contenere più di una parola, come una lista di nomi e cognomi. E si desidera mettere in maiuscolo la prima lettera di ciascuna di queste parole:
Main.java
123456789101112131415161718192021222324package com.example; import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<String> users = Arrays.asList("Ethan Johnson", "Olivia smith", "mason davis", "Ava taylor", "logan brown", "Emma Anderson", "jackson miller"); System.out.println("List of users: " + users); List<String> list = users.stream() .flatMap(e -> Arrays.stream(e.split(" "))) .map(e -> capitalizeFirstLetter(e)) .toList(); System.out.println("List with capitalized names and surnames: " + list); } private static String capitalizeFirstLetter(String word) { if (word == null || word.isEmpty()) { return word; } return Character.toUpperCase(word.charAt(0)) + word.substring(1); } }
Nel codice sopra, è stato scritto un metodo privato separato che mette in maiuscolo la prima lettera di una parola e questo metodo è stato utilizzato nel metodo map() insieme a una espressione lambda.
Si noti che utilizzando il metodo flatMap, ogni elemento dello stream viene suddiviso in diversi stream tramite il metodo Arrays.stream(e.split(" ")). Poiché il metodo split() restituisce un array, è necessario utilizzare il metodo Arrays.stream() per suddividere questo array in stream.
Successivamente, tutti questi stream vengono uniti in un unico stream, dopodiché si utilizza il metodo scritto in precedenza. Ora si dispone di tutti i nomi e cognomi degli utenti con la prima lettera maiuscola.
Sai cosa sarebbe interessante?
Se inserissimo questi nomi e cognomi in una HashMap, dove la chiave è il cognome e il valore è il nome.
Implementiamo questo nel codice:
Main.java
12345678910111213141516171819202122232425262728293031323334package com.example; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; public class Main { public static void main(String[] args) { List<String> users = Arrays.asList("Ethan Johnson", "Olivia smith", "mason davis", "Ava taylor", "logan brown", "Emma Anderson", "jackson miller"); System.out.println("List of users: " + users); List<String> list = users.stream() .flatMap(e -> Arrays.stream(e.split(" "))) .map(e -> capitalizeFirstLetter(e)) .toList(); System.out.println("List with capitalized names and surnames: " + list); Map<String, String> usersKeyValue = new HashMap<>(); for (int i = 0; i < list.size() - 1; i+=2) { String name = list.get(i); String surname = list.get(i + 1); usersKeyValue.put(surname, name); } System.out.println("Map with surnames as keys and names as values: " + usersKeyValue); } private static String capitalizeFirstLetter(String word) { if (word == null || word.isEmpty()) { return word; } return Character.toUpperCase(word.charAt(0)) + word.substring(1); } }
Con un semplice ciclo, abbiamo memorizzato il nome e il cognome in variabili e poi nella mappa. Nota come funziona il ciclo. Incrementiamo la variabile i di 2 a ogni iterazione perché dobbiamo saltare il cognome una volta che lo abbiamo già registrato.
- Il metodo
distinct()rimuove i duplicati dallo stream. In generale, questo può essere utile se sono necessari elementi unici nello stream o se si desidera eliminare rapidamente i duplicati da una lista. È possibile ottenere facilmente questo risultato con la seguente struttura:
list.stream().distinct().toList()
-
Il metodo
sortedordina tutti gli elementi nello stream in ordine naturale, dal numero più piccolo al più grande o in ordine alfabetico. Questo può essere utile anche se è necessario uno stream ordinato o se si desidera ordinare rapidamente una lista; -
Il metodo
skip(n)salta i priminelementi dello stream. Questo può essere utile quando si lavora con file di testo, dove le prime n righe potrebbero essere, ad esempio, metadati o una descrizione del file. Vale anche la pena menzionare il metodolimit(n), che generalmente limita il numero di elementi nello stream. Anche se si crea uno stream con 1000 elementi e poi si utilizzalimit(200), lo stream conterrà solo i primi 200 elementi.
Main.java
123456789101112package com.example; import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<Integer> example = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); example = example.stream().skip(3).limit(5).toList(); System.out.println("List: " + example); } }
Questi sono i principali metodi intermedi da utilizzare. È possibile esplorare gli altri metodi facendo riferimento al link alla documentazione ufficiale di Java. Passiamo ora ai metodi terminali.
Metodi Terminali
-
Il metodo terminale già noto è
toList(). Converte lo stream in una lista e la restituisce. In altre parole, è possibile assegnare direttamente questo stream con metodi a una lista. Questo metodo è stato introdotto in Java 17 e sostituisce la struttura più complessacollect(Collectors.toList()); -
Il metodo
collect()converte anch'esso lo stream in una specifica struttura dati. Utilizza, come parametro, un metodo dell'interfacciaCollectors. Questa interfaccia dispone di metodi cometoList(),toSet()etoCollection(). Ad esempio:
Main.java
123456789101112131415package com.example; import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.stream.Collectors; public class Main { public static void main(String[] args) { List<Integer> example = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); Set<Integer> integerSet = example.stream().collect(Collectors.toSet()); System.out.println("List: " + example); System.out.println("Set: " + integerSet); } }
Il metodo forEach() accetta un'espressione lambda ed esegue una specifica azione per ogni elemento nello stream.
Ad esempio:
Main.java
1234567891011package com.example; import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<Integer> example = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); example.stream().forEach(e -> System.out.println(e + 1)); } }
La differenza tra questo metodo e il metodo map è che questo metodo è terminale, e dopo di esso, non è possibile chiamare altri metodi.
Questi sono tutti i metodi di base per lavorare con gli stream. È un argomento complesso e potresti non comprenderlo immediatamente. Tuttavia, è un argomento che si apprende con la pratica. Nei prossimi capitoli pratici sugli stream, avrai molte opportunità di lavorarci, poiché è un modo molto comodo e pratico per manipolare liste e array di dati!
1. Qual è lo scopo principale della Stream API in Java?
2. Quale delle seguenti è un'operazione terminale nella Stream API?
3. Cosa fa l'operazione map nell'API Stream?
4. In cosa differisce l'operazione flatMap da map nell'API Stream?
5. Cosa fa l'operazione filter nell'API Stream?
6. Qual è lo scopo dell'operazione forEach nella Stream API?
7. Quale delle seguenti è un'operazione intermedia nella Stream API?
8. Come viene utilizzata l'operazione limit nella Stream API?
Grazie per i tuoi commenti!
Chieda ad AI
Chieda ad AI
Chieda pure quello che desidera o provi una delle domande suggerite per iniziare la nostra conversazione
Can you explain more about how lambda expressions work with streams?
What are some common use cases for the Stream API in real-world Java projects?
Could you provide more examples of intermediate and terminal operations?
Fantastico!
Completion tasso migliorato a 4
API Stream in Java
Scorri per mostrare il menu
Esistono diversi modi per elaborare i dati in Java: cicli, metodi e vari algoritmi. Tuttavia, con Java 8 è stato introdotto uno strumento molto potente: la Stream API.
In termini semplici, la Stream API è un modo per lavorare in modo rapido e semplice con un flusso di informazioni. In questo caso, il flusso di informazioni è rappresentato dalle collezioni. La Stream API introduce alcuni concetti. Ecco i principali.
Concetti principali
-
Stream: rappresenta una sequenza di elementi di dati che possono essere elaborati;
-
Operazioni intermedie: operazioni che creano un nuovo stream dopo la loro esecuzione. Esempi:
filter,map,distinct,sorted; -
Operazioni terminali: operazioni che completano l'elaborazione dello stream e restituiscono un risultato. Esempi:
collect,forEach,count,reduce; -
Stream paralleli: consentono l'elaborazione parallela dei dati. I metodi
parallel()eparallelStream()vengono utilizzati per creare stream paralleli.
Basta parlare di teoria, iniziamo a programmare!
La dichiarazione di uno stream si effettua utilizzando un metodo sulla collezione che si desidera trasformare in uno stream:
Main.java
12List<String> strings = Arrays.asList("a", "b", "c"); Stream<String> stream = strings.stream();
Con il metodo stream(), si ottiene uno stream di stringhe. Tuttavia, per iniziare a lavorare con lo stream, è necessario comprendere cosa sono le espressioni lambda, poiché i metodi degli stream funzionano principalmente con esse.
Espressioni Lambda
Le espressioni lambda sono state introdotte in Java 8 e rappresentano una forma semplificata per creare funzioni anonime in Java. Non abbiamo trattato prima le funzioni anonime poiché non erano particolarmente necessarie, ma ora le conosceremo attraverso le espressioni lambda.
Sintassi delle Espressioni Lambda:
La sintassi generale per le espressioni lambda in Java è la seguente:
Example.java
123(parameters) -> expression // or (parameters) -> { statements; }
-
Parametri: elenco di parametri che può essere vuoto oppure contenere uno o più parametri;
-
Freccia: rappresentata dal simbolo
->, che separa i parametri dal corpo dell'espressione lambda; -
Espressione o istruzioni: corpo della funzione, che contiene un'espressione o un blocco di istruzioni.
Ecco un esempio di espressione lambda che rappresenta una funzione semplice che somma due numeri:
Example.java
12345678910// Traditional way MathOperation addition = new MathOperation() { @Override public int operate(int a, int b) { return a + b; } }; // Using a lambda expression MathOperation addition = (int a, int b) -> a + b;
Analizziamo più da vicino cosa succede esattamente nel codice sopra e come utilizziamo le espressioni lambda:
Main.java
1234567891011121314package com.example; // Functional interface with a single abstract method interface MyMathOperation { int operate(int a, int b); } public class Main { public static void main(String[] args) { // Using a lambda expression to implement the interface MyMathOperation addition = (a, b) -> a + b; System.out.println("Sum: " + addition.operate(5, 3)); } }
Nel codice sopra:
È stata creata un'interfaccia funzionale MyMathOperation con un singolo metodo astratto operate.
È stata utilizzata un'espressione lambda per implementare questo metodo, eseguendo la somma di due numeri.
È stato stampato il risultato della somma.
Si comprende che potrebbe essere difficile da capire cosa stia accadendo in questo codice per ora, ma torniamo alla Stream API, dove le espressioni lambda sono frequentemente utilizzate, e cerchiamo di capire come usarle nella pratica.
Come ricordato in precedenza, è stato creato uno stream di stringhe da una lista di stringhe. Ora, utilizziamo i metodi dello stream per rendere ogni stringa di questo stream maiuscola:
Main.java
12345678910111213package com.example; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("a", "b", "c"); Stream<String> stream = strings.stream(); stream.map(e -> e.toUpperCase()).toList(); } }
Nel codice sopra, è stata utilizzata un'espressione lambda e due metodi: map() e toList(). Se il funzionamento del metodo toList() è chiaro, il metodo map() modifica ogni elemento nello stream secondo la lambda expression fornita.
Analizziamo più da vicino come funziona l'espressione lambda in questo contesto:
Il metodo map() applica il metodo toUpperCase() a ciascun elemento dello stream. L'elemento di questo stream è stato definito come e e, utilizzando la lambda expression, si è indicato al programma di applicare questo metodo a ogni elemento.
Tuttavia, questa è solo un'operazione intermedia. Ciò significa che le operazioni sullo stream non sono ancora concluse. Per completare il lavoro sullo stream, è necessario applicare un'operazione terminale, che terminerà le operazioni sullo stream e restituirà un valore specifico. Ad esempio, è possibile utilizzare il metodo toList(), e lo stream modificato verrà convertito in una lista.
Ad esempio:
Main.java
1234567891011121314package com.example; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("a", "b", "c"); Stream<String> stream = strings.stream(); List<String> list = stream.map(e -> e.toUpperCase()).toList(); System.out.println(list); } }
Esaminiamo più da vicino le possibili operazioni intermedie nello stream.
Operazioni Intermedie
Il metodo map() - questo metodo è già noto; esegue le operazioni specificate dall'espressione lambda su ogni elemento dello stream.
Ad esempio, utilizziamo il metodo substring() su ciascun elemento nello stream di stringhe:
Main.java
123456789101112131415package com.example; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("Unlock", "Infinity", "with", "Codefinity"); System.out.println("List of strings: " + strings); Stream<String> stream = strings.stream(); List<String> list = stream.map(e -> e.substring(1, 4)).toList(); System.out.println("Modified list: " + list); } }
Il metodo filter() accetta un espressione lambda con una condizione in base alla quale lo stream verrà filtrato. In altre parole, tutti gli elementi che soddisfano la condizione rimarranno nello stream, mentre gli elementi che non soddisfano la condizione verranno rimossi dallo stream. Modifichiamo lo stream per mantenere solo gli elementi la cui lunghezza è maggiore di 5:
Main.java
12345678910111213141516package com.example; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("Unlock", "Infinity", "with", "Codefinity"); System.out.println("List of strings: " + strings); Stream<String> stream = strings.stream(); stream = stream.filter(e -> e.length() > 5); List<String> list = stream.map(e -> e.substring(1, 4)).toList(); System.out.println("Modified list: " + list); } }
Utilizzando il metodo filter(), la stringa "with" viene rimossa dallo stream perché questa parola contiene meno di 5 caratteri.
È inoltre possibile utilizzare operazioni intermedie più volte di seguito.
Ad esempio, è possibile semplificare leggermente il codice sopra riportato:
Main.java
12345678910111213141516package com.example; import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("Unlock", "Infinity", "with", "Codefinity"); System.out.println("List of strings: " + strings); List<String> list = strings.stream() .filter(e -> e.length() > 5) .map(e -> e.substring(1, 4)) .toList(); System.out.println("Modified list: " + list); } }
Quando si concatenano più metodi stream insieme, si consiglia di posizionare ciascun metodo su una nuova riga per migliorare significativamente la leggibilità del codice.
Il metodo flatMap() trasforma ogni elemento di uno stream in un nuovo stream e combina i risultati in un unico stream. In altre parole, con questo metodo, è possibile suddividere lo stream in più stream, che poi verranno uniti in un unico stream. Ad esempio, si dispone di una lista di stringhe in cui ciascuna stringa può contenere più di una parola, come una lista di nomi e cognomi. E si desidera mettere in maiuscolo la prima lettera di ciascuna di queste parole:
Main.java
123456789101112131415161718192021222324package com.example; import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<String> users = Arrays.asList("Ethan Johnson", "Olivia smith", "mason davis", "Ava taylor", "logan brown", "Emma Anderson", "jackson miller"); System.out.println("List of users: " + users); List<String> list = users.stream() .flatMap(e -> Arrays.stream(e.split(" "))) .map(e -> capitalizeFirstLetter(e)) .toList(); System.out.println("List with capitalized names and surnames: " + list); } private static String capitalizeFirstLetter(String word) { if (word == null || word.isEmpty()) { return word; } return Character.toUpperCase(word.charAt(0)) + word.substring(1); } }
Nel codice sopra, è stato scritto un metodo privato separato che mette in maiuscolo la prima lettera di una parola e questo metodo è stato utilizzato nel metodo map() insieme a una espressione lambda.
Si noti che utilizzando il metodo flatMap, ogni elemento dello stream viene suddiviso in diversi stream tramite il metodo Arrays.stream(e.split(" ")). Poiché il metodo split() restituisce un array, è necessario utilizzare il metodo Arrays.stream() per suddividere questo array in stream.
Successivamente, tutti questi stream vengono uniti in un unico stream, dopodiché si utilizza il metodo scritto in precedenza. Ora si dispone di tutti i nomi e cognomi degli utenti con la prima lettera maiuscola.
Sai cosa sarebbe interessante?
Se inserissimo questi nomi e cognomi in una HashMap, dove la chiave è il cognome e il valore è il nome.
Implementiamo questo nel codice:
Main.java
12345678910111213141516171819202122232425262728293031323334package com.example; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; public class Main { public static void main(String[] args) { List<String> users = Arrays.asList("Ethan Johnson", "Olivia smith", "mason davis", "Ava taylor", "logan brown", "Emma Anderson", "jackson miller"); System.out.println("List of users: " + users); List<String> list = users.stream() .flatMap(e -> Arrays.stream(e.split(" "))) .map(e -> capitalizeFirstLetter(e)) .toList(); System.out.println("List with capitalized names and surnames: " + list); Map<String, String> usersKeyValue = new HashMap<>(); for (int i = 0; i < list.size() - 1; i+=2) { String name = list.get(i); String surname = list.get(i + 1); usersKeyValue.put(surname, name); } System.out.println("Map with surnames as keys and names as values: " + usersKeyValue); } private static String capitalizeFirstLetter(String word) { if (word == null || word.isEmpty()) { return word; } return Character.toUpperCase(word.charAt(0)) + word.substring(1); } }
Con un semplice ciclo, abbiamo memorizzato il nome e il cognome in variabili e poi nella mappa. Nota come funziona il ciclo. Incrementiamo la variabile i di 2 a ogni iterazione perché dobbiamo saltare il cognome una volta che lo abbiamo già registrato.
- Il metodo
distinct()rimuove i duplicati dallo stream. In generale, questo può essere utile se sono necessari elementi unici nello stream o se si desidera eliminare rapidamente i duplicati da una lista. È possibile ottenere facilmente questo risultato con la seguente struttura:
list.stream().distinct().toList()
-
Il metodo
sortedordina tutti gli elementi nello stream in ordine naturale, dal numero più piccolo al più grande o in ordine alfabetico. Questo può essere utile anche se è necessario uno stream ordinato o se si desidera ordinare rapidamente una lista; -
Il metodo
skip(n)salta i priminelementi dello stream. Questo può essere utile quando si lavora con file di testo, dove le prime n righe potrebbero essere, ad esempio, metadati o una descrizione del file. Vale anche la pena menzionare il metodolimit(n), che generalmente limita il numero di elementi nello stream. Anche se si crea uno stream con 1000 elementi e poi si utilizzalimit(200), lo stream conterrà solo i primi 200 elementi.
Main.java
123456789101112package com.example; import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<Integer> example = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); example = example.stream().skip(3).limit(5).toList(); System.out.println("List: " + example); } }
Questi sono i principali metodi intermedi da utilizzare. È possibile esplorare gli altri metodi facendo riferimento al link alla documentazione ufficiale di Java. Passiamo ora ai metodi terminali.
Metodi Terminali
-
Il metodo terminale già noto è
toList(). Converte lo stream in una lista e la restituisce. In altre parole, è possibile assegnare direttamente questo stream con metodi a una lista. Questo metodo è stato introdotto in Java 17 e sostituisce la struttura più complessacollect(Collectors.toList()); -
Il metodo
collect()converte anch'esso lo stream in una specifica struttura dati. Utilizza, come parametro, un metodo dell'interfacciaCollectors. Questa interfaccia dispone di metodi cometoList(),toSet()etoCollection(). Ad esempio:
Main.java
123456789101112131415package com.example; import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.stream.Collectors; public class Main { public static void main(String[] args) { List<Integer> example = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); Set<Integer> integerSet = example.stream().collect(Collectors.toSet()); System.out.println("List: " + example); System.out.println("Set: " + integerSet); } }
Il metodo forEach() accetta un'espressione lambda ed esegue una specifica azione per ogni elemento nello stream.
Ad esempio:
Main.java
1234567891011package com.example; import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<Integer> example = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); example.stream().forEach(e -> System.out.println(e + 1)); } }
La differenza tra questo metodo e il metodo map è che questo metodo è terminale, e dopo di esso, non è possibile chiamare altri metodi.
Questi sono tutti i metodi di base per lavorare con gli stream. È un argomento complesso e potresti non comprenderlo immediatamente. Tuttavia, è un argomento che si apprende con la pratica. Nei prossimi capitoli pratici sugli stream, avrai molte opportunità di lavorarci, poiché è un modo molto comodo e pratico per manipolare liste e array di dati!
1. Qual è lo scopo principale della Stream API in Java?
2. Quale delle seguenti è un'operazione terminale nella Stream API?
3. Cosa fa l'operazione map nell'API Stream?
4. In cosa differisce l'operazione flatMap da map nell'API Stream?
5. Cosa fa l'operazione filter nell'API Stream?
6. Qual è lo scopo dell'operazione forEach nella Stream API?
7. Quale delle seguenti è un'operazione intermedia nella Stream API?
8. Come viene utilizzata l'operazione limit nella Stream API?
Grazie per i tuoi commenti!