1. Trasformazione degli elementi delle collezioni
In programmazione una delle operazioni più frequenti è la trasformazione di una collezione: abbiamo una collezione di dati di un tipo e, sulla sua base, dobbiamo crearne una nuova di un altro tipo. Per esempio, da un elenco di oggetti di tipo User ottenere l’elenco dei loro nomi (String), oppure da un elenco di numeri l’elenco dei loro quadrati.
Il modo più fondamentale e comprensibile in Java è usare l’approccio imperativo, cioè un normale ciclo for (spesso — for-each).
Esempio: ottenere la lista delle lunghezze a partire da una lista di stringhe
Supponiamo di avere un elenco di nomi di città:
List<String> cities = List.of("Londra", "Parigi", "Tokyo", "New York");
Il nostro compito è creare un nuovo elenco che contenga la lunghezza di ogni nome. Il risultato finale dovrebbe essere: [6, 6, 5, 8].
Soluzione con un ciclo for:
import java.util.ArrayList;
import java.util.List;
public class CollectionTransform {
public static void main(String[] args) {
List<String> cities = List.of("Londra", "Parigi", "Tokyo", "New York");
List<Integer> lengths = new ArrayList<>(); // Creiamo una nuova lista vuota per il risultato
for (String city : cities) {
// Per ogni elemento di cities calcoliamo la sua lunghezza...
int length = city.length();
// ...e aggiungiamo questo risultato al nuovo elenco
lengths.add(length);
}
System.out.println(lengths); // Stamperà: [6, 6, 5, 8]
}
}
Iniziamo creando una nuova collezione vuota per i risultati — la trasformazione non modifica la collezione di origine. Per iterare sull’originale usiamo un ciclo for-each e all’interno applichiamo la logica necessaria (length()) a ogni elemento, aggiungendo il risultato al nuovo elenco tramite add.
2. Trasformazione di oggetti
Spesso lavoriamo con tipi di dati più complessi. Supponiamo di avere una classe Product e di dover ottenere l’elenco dei suoi nomi (o dei prezzi).
public class Product {
private String name;
private double price;
public Product(String name, double price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public double getPrice() {
return price;
}
}
Ora, avendo l’elenco dei prodotti, otteniamo l’elenco dei loro nomi:
import java.util.ArrayList;
import java.util.List;
public class ProductExample {
public static void main(String[] args) {
List<Product> products = List.of(
new Product("Portatile", 1200.0),
new Product("Mouse", 25.5),
new Product("Tastiera", 75.0)
);
// Creiamo una nuova lista per i nomi
List<String> productNames = new ArrayList<>();
for (Product product : products) {
// Per ogni oggetto Product otteniamo il suo nome
productNames.add(product.getName());
}
System.out.println(productNames); // Stamperà: [Portatile, Mouse, Tastiera]
}
}
La logica è la stessa: iteriamo sulla collezione di origine, applichiamo a ogni elemento il metodo desiderato (ad esempio, getName()) e aggiungiamo il risultato alla nuova collezione.
3. Lavorare con collezioni annidate
Consideriamo il caso di un elenco di elenchi: più reparti, ognuno con il proprio elenco di dipendenti. L’obiettivo è ottenere un unico elenco («piatto») di tutti i dipendenti.
Esempio: unione degli elenchi di dipendenti
List<List<String>> departments = List.of(
List.of("Anna", "Boris"),
List.of("Viktoriya", "Gleb", "Dmitriy"),
List.of("Elena")
);
Vogliamo ottenere un’unica lista: [Anna, Boris, Viktoriya, Gleb, Dmitriy, Elena].
Metodo 1: con il metodo addAll()
import java.util.ArrayList;
import java.util.List;
public class NestedCollectionExample {
public static void main(String[] args) {
List<List<String>> departments = List.of(
List.of("Anna", "Boris"),
List.of("Viktoriya", "Gleb", "Dmitriy"),
List.of("Elena")
);
List<String> allEmployees = new ArrayList<>();
// Iteriamo su ogni lista (reparto)
for (List<String> department : departments) {
// Aggiungiamo tutti gli elementi della lista corrente all’elenco generale
allEmployees.addAll(department);
}
System.out.println(allEmployees); // [Anna, Boris, Viktoriya, Gleb, Dmitriy, Elena]
}
}
Metodo 2: ciclo annidato — lo stesso, ma «a mano»:
List<List<String>> departments = List.of(...);
List<String> allEmployees = new ArrayList<>();
for (List<String> department : departments) { // Ciclo esterno sui reparti
for (String employee : department) { // Ciclo interno sui dipendenti
allEmployees.add(employee);
}
}
System.out.println(allEmployees);
Entrambi i metodi sono equivalenti nel risultato. Il metodo addAll() in sostanza incapsula la logica del ciclo annidato e rende il codice più conciso.
4. Casi complessi e trasformazioni con condizioni
A volte la trasformazione va eseguita solo per gli elementi che soddisfano una condizione. Per esempio, ottenere l’elenco dei nomi delle città che iniziano con la lettera "N" e prenderne le lunghezze. Qui combiniamo filtraggio e trasformazione: if + chiamata del metodo (length()).
import java.util.ArrayList;
import java.util.List;
public class ConditionalTransform {
public static void main(String[] args) {
List<String> cities = List.of("Londra", "Parigi", "Tokyo", "New York", "Norimberga");
List<Integer> lengths = new ArrayList<>();
for (String city : cities) {
// Prima controlliamo la condizione
if (city.startsWith("N")) {
// Se la condizione è soddisfatta, applichiamo la trasformazione
lengths.add(city.length());
}
}
System.out.println(lengths); // Stamperà: [8, 10]
}
}
Il modello è questo: all’interno del ciclo prima filtriamo l’elemento tramite una condizione (startsWith, confronto, controllo di intervallo, ecc.), poi applichiamo la trasformazione necessaria e mettiamo il risultato nel nuovo elenco.
5. Errori tipici e insidie
Errore n. 1: modificare la collezione di origine durante l’iterazione. Un errore frequente è tentare di aggiungere/rimuovere elementi dalla collezione originaria direttamente nel ciclo for-each. Questo porta a errori e comportamenti imprevedibili. Soluzione: create sempre un nuovo elenco per i risultati e riempitelo.
Errore n. 2: cast errato. Se lavorate con collezioni “raw” (per esempio tramite Object) e fate il cast a un tipo errato, otterrete ClassCastException. Usate i generics (List<T>) e rispettate le firme dei metodi.
Errore n. 3: uso inefficiente delle risorse. Per collezioni molto grandi, creare continuamente nuovi elenchi e copiare elementi può incidere su memoria e tempo. Nella maggior parte dei compiti quotidiani è accettabile, ma quando trattate grandi volumi di dati considerate la complessità e, se necessario, ottimizzate l’approccio.
GO TO FULL VERSION