1. Transformación de elementos de colecciones
En programación, una de las operaciones más frecuentes es la transformación de colecciones: tenemos una colección de datos de un tipo y, a partir de ella, debemos crear una nueva colección de otro tipo. Por ejemplo, a partir de una lista de objetos de tipo User obtener la lista de sus nombres (String), o a partir de una lista de números — la lista de sus cuadrados.
La forma más fundamental y comprensible en Java es usar el enfoque imperativo, es decir, un bucle for (a menudo — for-each).
Ejemplo: obtener la lista de longitudes a partir de una lista de cadenas
Supongamos que tenemos una lista de nombres de ciudades:
List<String> cities = List.of("Londres", "París", "Tokio", "Nueva York");
Nuestro objetivo es crear una nueva lista que contenga la longitud de cada nombre. El resultado final debería ser: [7, 5, 5, 10].
Solución con un bucle for:
import java.util.ArrayList;
import java.util.List;
public class CollectionTransform {
public static void main(String[] args) {
List<String> cities = List.of("Londres", "París", "Tokio", "Nueva York");
List<Integer> lengths = new ArrayList<>(); // Creamos una nueva lista vacía para el resultado
for (String city : cities) {
// Para cada elemento de cities calculamos su longitud...
int length = city.length();
// ...y añadimos ese resultado a la nueva lista
lengths.add(length);
}
System.out.println(lengths); // Mostrará: [7, 5, 5, 10]
}
}
Empezamos creando una nueva colección vacía para los resultados — la transformación no modifica la colección original. Para recorrer la colección de origen usamos un bucle for-each y dentro aplicamos la lógica necesaria (length()) a cada elemento, y añadimos el resultado a la nueva lista con add.
2. Transformación de objetos
A menudo trabajamos con tipos de datos más complejos. Supongamos que tenemos una clase Product, y necesitamos obtener la lista de sus nombres (o de sus precios).
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;
}
}
Ahora, teniendo una lista de productos, obtendremos la lista de sus nombres:
import java.util.ArrayList;
import java.util.List;
public class ProductExample {
public static void main(String[] args) {
List<Product> products = List.of(
new Product("Portátil", 1200.0),
new Product("Ratón", 25.5),
new Product("Teclado", 75.0)
);
// Creamos una nueva lista para los nombres
List<String> productNames = new ArrayList<>();
for (Product product : products) {
// Para cada objeto Product obtenemos su nombre
productNames.add(product.getName());
}
System.out.println(productNames); // Mostrará: [Portátil, Ratón, Teclado]
}
}
La lógica es la misma: iteramos por la colección de origen, aplicamos a cada elemento el método necesario (por ejemplo, getName()) y añadimos el resultado a la nueva colección.
3. Trabajo con colecciones anidadas
Consideremos el caso de una lista de listas: varios departamentos, cada uno con su propia lista de empleados. La tarea es obtener una única lista («plana») con todos los empleados.
Ejemplo: unir listas de empleados
List<List<String>> departments = List.of(
List.of("Anna", "Boris"),
List.of("Victoria", "Gleb", "Dmitry"),
List.of("Elena")
);
Queremos obtener una única lista: [Anna, Boris, Victoria, Gleb, Dmitry, Elena].
Método 1: mediante el método 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("Victoria", "Gleb", "Dmitry"),
List.of("Elena")
);
List<String> allEmployees = new ArrayList<>();
// Iteramos por cada lista (departamento)
for (List<String> department : departments) {
// Añadimos todos los elementos de la lista actual a la lista general
allEmployees.addAll(department);
}
System.out.println(allEmployees); // [Anna, Boris, Victoria, Gleb, Dmitry, Elena]
}
}
Método 2: bucle anidado — lo mismo, pero «a mano»:
List<List<String>> departments = List.of(...);
List<String> allEmployees = new ArrayList<>();
for (List<String> department : departments) { // Bucle externo por departamentos
for (String employee : department) { // Bucle interno por empleados
allEmployees.add(employee);
}
}
System.out.println(allEmployees);
Ambos métodos son equivalentes en cuanto al resultado. El método addAll() básicamente encapsula la lógica del bucle anidado y hace el código más corto.
4. Casos complejos y transformaciones con condiciones
A veces la transformación debe realizarse solo para los elementos que cumplen una condición. Por ejemplo, obtener la lista de nombres de ciudades que empiezan por la letra "N", y tomar sus longitudes. Aquí combinamos filtrado y transformación: if + llamada a método (length()).
import java.util.ArrayList;
import java.util.List;
public class ConditionalTransform {
public static void main(String[] args) {
List<String> cities = List.of("Londres", "París", "Tokio", "Nueva York", "Núremberg");
List<Integer> lengths = new ArrayList<>();
for (String city : cities) {
// Primero comprobamos la condición
if (city.startsWith("N")) {
// Si se cumple la condición, aplicamos la transformación
lengths.add(city.length());
}
}
System.out.println(lengths); // Mostrará: [10, 9]
}
}
El patrón es el siguiente: dentro del bucle primero filtramos el elemento mediante una condición (startsWith, comparación, comprobación de rango, etc.), luego aplicamos la transformación necesaria y ponemos el resultado en la nueva lista.
5. Errores típicos y escollos
Error n.º 1: modificar la colección de origen durante la iteración. Un error frecuente es intentar añadir/eliminar elementos de la colección de origen directamente dentro de un bucle for-each. Esto conduce a errores y a comportamientos impredecibles. Solución: cree siempre una lista nueva para los resultados y rellénela.
Error n.º 2: conversiones de tipo incorrectas. Si trabaja con colecciones «raw» (por ejemplo, a través de Object) y convierte los elementos a un tipo incorrecto, obtendrá ClassCastException. Utilice genéricos (List<T>) y preste atención a las firmas.
Error n.º 3: uso ineficiente de recursos. Para colecciones muy grandes, la creación constante de nuevas listas y la copia de elementos puede notarse en memoria y tiempo. En la mayoría de tareas cotidianas es aceptable, pero al procesar grandes volúmenes de datos tenga en cuenta la complejidad y, si es necesario, optimice el enfoque.
GO TO FULL VERSION