CodeGym /Corsi /JAVA 25 SELF /Uso delle lambda nelle collezioni e negli stream

Uso delle lambda nelle collezioni e negli stream

JAVA 25 SELF
Livello 48, Lezione 1
Disponibile

1. Espressioni lambda e collezioni

Ripassiamo come appariva l’elaborazione delle collezioni prima dell’arrivo delle espressioni lambda. Supponiamo di avere un elenco di stringhe e di volerle stampare a schermo:

List<String> list = Arrays.asList("gatto", "cane", "riccio");

for (String s : list) {
    System.out.println(s);
}

Tutto semplice, ma se vogliamo, per esempio, eliminare dall’elenco tutte le stringhe vuote, dobbiamo scrivere un ciclo con condizione e talvolta usare un iteratore (altrimenti ci sarà una ConcurrentModificationException). Oppure, per esempio, ordinare per lunghezza della stringa:

Collections.sort(list, new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return a.length() - b.length();
    }
});

Anche per un compito così semplice — già 5 righe di codice e un mucchio di parentesi «rumorose». Come ottimizzare? La risposta la conoscete già: espressioni lambda.

Uso delle espressioni lambda nei metodi delle collezioni

A partire da Java 8 le interfacce delle collezioni hanno ricevuto nuovi metodi che accettano interfacce funzionali — il che significa che possiamo passarvi espressioni lambda. Ecco i più popolari:

  • forEach(Consumer<T> action)
  • removeIf(Predicate<T> filter)
  • sort(Comparator<T> c)
  • replaceAll(UnaryOperator<T> operator)

Esempio: forEach

Stampare tutti gli elementi della lista a schermo (modo tradizionale):

for (String s : list) {
    System.out.println(s);
}

Ora — con una lambda:

list.forEach(s -> System.out.println(s));

Oppure ancora più corto, se volete giocare a fare il «guru di Java»:

list.forEach(System.out::println); // method reference, lo vedremo più avanti

Esempio: removeIf

Rimuovere tutte le stringhe vuote dalla lista:

List<String> animals = new ArrayList<>(Arrays.asList("gatto", "", "cane", "riccio", ""));
animals.removeIf(s -> s.isEmpty());
System.out.println(animals); // [gatto, cane, riccio]

Esempio: sort

Ordinamento della lista per lunghezza della stringa:

List<String> animals = Arrays.asList("gatto", "cane", "riccio", "elefante");

animals.sort((a, b) -> a.length() - b.length());
System.out.println(animals); // [cane, gatto, riccio, elefante]

Esempio: replaceAll

Convertire tutte le stringhe in maiuscolo:

List<String> animals = new ArrayList<>(Arrays.asList("gatto", "cane", "riccio"));
animals.replaceAll(s -> s.toUpperCase());
System.out.println(animals); // [GATTO, CANE, RICCIO]

2. Stream API ed espressioni lambda

Con l’uscita di Java 8 è apparso lo Stream API — uno strumento potente per l’elaborazione delle collezioni in stile funzionale. Gli stream permettono di filtrare, trasformare, ordinare, raccogliere collezioni tramite catene di metodi. E tutti questi metodi accettano espressioni lambda!

Importante: L’analisi completa dello Stream API arriverà più avanti; per ora — solo esempi di base per capire il ruolo delle lambda.

Esempio: filtraggio

Lasciare solo le stringhe più lunghe di 3 caratteri:

List<String> animals = Arrays.asList("gatto", "elefante", "riccio", "coccodrillo");
animals.stream()
       .filter(s -> s.length() > 3)
       .forEach(System.out::println); // gatto, elefante, riccio, coccodrillo

Esempio: trasformazione (map)

Rendere tutte le stringhe in maiuscolo:

List<String> animals = Arrays.asList("gatto", "elefante", "riccio");
List<String> upper = animals.stream()
                            .map(s -> s.toUpperCase())
                            .collect(Collectors.toList());
System.out.println(upper); // [GATTO, ELEFANTE, RICCIO]

Esempio: ordinamento

Ottenere un elenco ordinato per lunghezza (senza modificare l’originale):

List<String> animals = Arrays.asList("gatto", "elefante", "riccio", "coccodrillo");
List<String> sorted = animals.stream()
                             .sorted((a, b) -> a.length() - b.length())
                             .collect(Collectors.toList());
System.out.println(sorted); // [gatto, riccio, elefante, coccodrillo]

3. Confronto con le classi anonime

Confrontiamo come apparirebbe lo stesso codice con una classe anonima e con una lambda.

Ordinamento per lunghezza della stringa

Classe anonima:

animals.sort(new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return a.length() - b.length();
    }
});

Espressione lambda:

animals.sort((a, b) -> a.length() - b.length());

Conclusione:
L’espressione lambda risparmia molte righe e rende il codice più leggibile. Meno parentesi, meno rumore — più sostanza!

Eliminazione delle stringhe vuote

Classe anonima:

animals.removeIf(new Predicate<String>() {
    @Override
    public boolean test(String s) {
        return s.isEmpty();
    }
});

Espressione lambda:

animals.removeIf(s -> s.isEmpty());

4. Pratica: brevi esercizi con espressioni lambda

Mettiamo in pratica le espressioni lambda in una mini‑app per lavorare con un elenco di utenti.

Esempio 1: Filtrare gli utenti per età

class User {
    String name;
    int age;
    User(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return name + " (" + age + ")";
    }
}

List<User> users = Arrays.asList(
    new User("Alice", 17),
    new User("Bob", 25),
    new User("Charlie", 15)
);

users.stream()
     .filter(u -> u.age >= 18)
     .forEach(System.out::println); // Bob (25)

Esempio 2: Ordinare gli utenti per nome

List<User> users = Arrays.asList(
    new User("Alice", 17),
    new User("Bob", 25),
    new User("Charlie", 15)
);

users.sort((u1, u2) -> u1.name.compareTo(u2.name));
System.out.println(users);
// [Alice (17), Bob (25), Charlie (15)]

Esempio 3: Trasformare l’elenco degli utenti in un elenco di nomi

List<String> names = users.stream()
                          .map(u -> u.name)
                          .collect(Collectors.toList());
System.out.println(names); // [Alice, Bob, Charlie]

Esempio 4: Rimuovere tutti i minorenni

List<User> users = new ArrayList<>(Arrays.asList(
    new User("Alice", 17),
    new User("Bob", 25),
    new User("Charlie", 15)
));

users.removeIf(u -> u.age < 18);
System.out.println(users); // [Bob (25)]

5. Sfumature utili

Particolarità: ambito e variabili «effettivamente final»

Un’espressione lambda può usare variabili dal metodo esterno, ma solo se sono final o «effettivamente final» (cioè non vengono modificate dopo l’inizializzazione).

int minAge = 18;
users.stream()
     .filter(u -> u.age >= minAge)
     .forEach(System.out::println);

Se provi a modificare minAge dopo averla usata nella lambda, il compilatore segnalerà un errore.

Tabella: metodi principali di collezioni e stream con espressioni lambda

Metodo di collezione/stream Cosa fa Tipo di espressione lambda Esempio
forEach(Consumer<T>)
Per ogni elemento
x -> ...
list.forEach(s -> ...)
removeIf(Predicate<T>)
Rimuove gli elementi in base a una condizione
x -> ...
list.removeIf(s -> ...)
sort(Comparator<T>)
Ordina gli elementi
(a, b) -> ...
list.sort((a, b) -> ...)
replaceAll(UnaryOperator<T>)
Sostituisce ogni elemento
x -> ...
list.replaceAll(s -> ...)
filter(Predicate<T>)
Filtra lo stream
x -> ...
stream.filter(s -> ...)
map(Function<T, R>)
Trasforma gli elementi
x -> ...
stream.map(s -> ...)
forEach(Consumer<T>)
Itera sullo stream
x -> ...
stream.forEach(s -> ...)
sorted(Comparator<T>)
Ordinamento nello stream
(a, b) -> ...
stream.sorted((a, b) -> ...)

7. Errori tipici

Errore n. 1: La lambda è troppo lunga. Se dentro un’espressione lambda hai già 5 righe di codice, condizioni, cicli e try-catch, molto probabilmente conviene estrarre quel codice in un metodo separato. Le lambda sono ottime per logiche brevi.

Errore n. 2: Uso di variabili mutabili. Se provi a modificare dentro la lambda una variabile del metodo esterno (per esempio, un contatore), il compilatore non lo permetterà. La variabile deve essere final o «effettivamente final».

Errore n. 3: Dimenticare che i metodi delle collezioni/stream non sempre modificano la collezione originale. Per esempio, stream().filter(...) non modifica la lista originale, ma restituisce un nuovo stream. Se vuoi ottenere una collezione — usa collect(Collectors.toList()).

Errore n. 4: L’espressione lambda non corrisponde al tipo richiesto. Se un metodo accetta, per esempio, un Comparator<T> e provi a passare una lambda con un solo parametro (invece di due), otterrai un errore di compilazione.

Errore n. 5: Perdita di leggibilità con lambda annidate. Se hai una catena di map, filter, forEach e dentro ogni lambda ce n’è un’altra, il codice diventa illeggibile. In questi casi è meglio dividere l’espressione in passaggi separati o estrarre parti in metodi.

1
Compito
JAVA 25 SELF, livello 48, lezione 1
Bloccato
Sistema "STRILLANTI" dei Prodotti 📢
Sistema "STRILLANTI" dei Prodotti 📢
1
Compito
JAVA 25 SELF, livello 48, lezione 1
Bloccato
Spedizione "Grandi Acque" 🗺️
Spedizione "Grandi Acque" 🗺️
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION