Java
Tutorial Java: Listas
Aprende la interfaz List y ArrayList en Java con ejemplos prácticos y cómo implementar operaciones CRUD para gestionar colecciones eficientemente.
Aprende Java y certifícateLa interfaz List
La interfaz List es uno de los componentes fundamentales del Framework Collections de Java. Esta interfaz define una colección ordenada que permite almacenar elementos duplicados, a diferencia de otras colecciones como los conjuntos (Set).
La principal característica de List es que mantiene un orden de inserción de los elementos y permite acceder a ellos mediante su posición o índice. Esto la hace ideal para situaciones donde necesitamos mantener una secuencia específica de elementos y posiblemente acceder a ellos por su posición.
Características principales de List
- Ordenada: Los elementos mantienen el orden en que fueron agregados
- Indexada: Permite acceder a elementos por su posición (índice)
- Permite duplicados: A diferencia de Set, List puede contener elementos repetidos
- Permite valores nulos: Puede contener referencias null (aunque no es recomendable)
Métodos principales de la interfaz List
La interfaz List extiende la interfaz Collection y añade métodos específicos para trabajar con colecciones ordenadas:
- add(E e): Añade un elemento al final de la lista
- add(int index, E element): Inserta un elemento en la posición especificada
- get(int index): Obtiene el elemento en la posición indicada
- set(int index, E element): Reemplaza el elemento en la posición indicada
- remove(int index): Elimina el elemento en la posición indicada
- indexOf(Object o): Devuelve la primera posición donde aparece el elemento
- lastIndexOf(Object o): Devuelve la última posición donde aparece el elemento
- subList(int fromIndex, int toIndex): Obtiene una sublista entre las posiciones indicadas
Implementaciones de List
Java proporciona varias implementaciones de la interfaz List, cada una con características específicas:
- ArrayList: Implementación basada en arrays dinámicos. Ofrece acceso rápido por índice.
- LinkedList: Implementación basada en listas doblemente enlazadas. Eficiente para inserciones y eliminaciones.
- Vector: Similar a ArrayList pero sincronizada (thread-safe). Considerada legacy.
- Stack: Extiende Vector e implementa una pila (LIFO). También considerada legacy.
Declaración y uso básico
Para utilizar una List en Java, primero debemos importar el paquete correspondiente:
import java.util.List;
import java.util.ArrayList; // u otra implementación
Luego podemos crear instancias utilizando la interfaz como tipo y una implementación concreta:
// Declaración usando la interfaz como tipo
List<String> nombres = new ArrayList<>();
// Añadir elementos
nombres.add("Ana");
nombres.add("Carlos");
nombres.add("Beatriz");
// Acceder a elementos por índice
String primerNombre = nombres.get(0); // "Ana"
// Insertar en posición específica
nombres.add(1, "Daniel"); // La lista ahora es: Ana, Daniel, Carlos, Beatriz
// Recorrer la lista
for (String nombre : nombres) {
System.out.println(nombre);
}
// Tamaño de la lista
int cantidad = nombres.size(); // 4
Uso de List con tipos primitivos
Para usar tipos primitivos con List, debemos utilizar sus clases envoltorio (wrapper classes):
// Lista de números enteros
List<Integer> numeros = new ArrayList<>();
numeros.add(10);
numeros.add(20);
numeros.add(30);
// Java realiza autoboxing/unboxing automáticamente
int primerNumero = numeros.get(0); // 10
Operaciones comunes con List
Verificar si un elemento existe
List<String> frutas = new ArrayList<>();
frutas.add("Manzana");
frutas.add("Banana");
frutas.add("Naranja");
boolean contieneManzana = frutas.contains("Manzana"); // true
boolean contienePera = frutas.contains("Pera"); // false
Eliminar elementos
List<String> colores = new ArrayList<>();
colores.add("Rojo");
colores.add("Verde");
colores.add("Azul");
colores.add("Verde"); // Elemento duplicado
// Eliminar por índice
colores.remove(0); // Elimina "Rojo"
// Eliminar por objeto (primera ocurrencia)
colores.remove("Verde"); // Elimina el primer "Verde"
// La lista ahora contiene: [Azul, Verde]
Convertir entre arrays y listas
// De array a lista
String[] arrayPaises = {"España", "Francia", "Italia"};
List<String> listaPaises = Arrays.asList(arrayPaises);
// De lista a array
String[] nuevoArray = listaPaises.toArray(new String[0]);
Iteración sobre una List
Existen varias formas de recorrer una List en Java:
List<Integer> numeros = new ArrayList<>();
numeros.add(10);
numeros.add(20);
numeros.add(30);
// Usando for tradicional con índice
for (int i = 0; i < numeros.size(); i++) {
System.out.println(numeros.get(i));
}
// Usando for-each (recomendado)
for (Integer numero : numeros) {
System.out.println(numero);
}
// Usando Iterator
Iterator<Integer> iterador = numeros.iterator();
while (iterador.hasNext()) {
System.out.println(iterador.next());
}
Ordenación de listas
La interfaz List proporciona un método sort()
para ordenar los elementos:
List<String> nombres = new ArrayList<>();
nombres.add("Zoe");
nombres.add("Ana");
nombres.add("Carlos");
// Ordenar alfabéticamente
Collections.sort(nombres);
// Resultado: [Ana, Carlos, Zoe]
// También podemos usar el método sort de List (Java 8+)
nombres.sort(Comparator.naturalOrder());
Ejemplo práctico: Gestión de tareas
Veamos un ejemplo práctico de cómo usar una List para gestionar una lista de tareas:
import java.util.ArrayList;
import java.util.List;
public class GestorTareas {
public static void main(String[] args) {
// Crear lista de tareas
List<String> tareas = new ArrayList<>();
// Añadir tareas
tareas.add("Estudiar Java");
tareas.add("Hacer ejercicio");
tareas.add("Comprar comida");
// Mostrar todas las tareas
System.out.println("Lista de tareas:");
for (int i = 0; i < tareas.size(); i++) {
System.out.println((i + 1) + ". " + tareas.get(i));
}
// Marcar una tarea como completada (eliminarla)
System.out.println("\nCompletando: " + tareas.get(1));
tareas.remove(1);
// Añadir nueva tarea con prioridad (al inicio)
tareas.add(0, "Llamar al médico");
// Mostrar lista actualizada
System.out.println("\nLista actualizada:");
for (int i = 0; i < tareas.size(); i++) {
System.out.println((i + 1) + ". " + tareas.get(i));
}
}
}
La salida de este programa sería:
Lista de tareas:
1. Estudiar Java
2. Hacer ejercicio
3. Comprar comida
Completando: Hacer ejercicio
Lista actualizada:
1. Llamar al médico
2. Estudiar Java
3. Comprar comida
La interfaz List es una herramienta versátil para manejar colecciones ordenadas en Java. Su capacidad para mantener el orden de inserción y permitir acceso por índice la hace ideal para muchos escenarios de programación donde necesitamos trabajar con secuencias de elementos.
La clase ArrayList
ArrayList es la implementación más utilizada de la interfaz List en Java. Esta clase proporciona una estructura de datos basada en un array dinámico que crece automáticamente según sea necesario, lo que la hace muy práctica para la mayoría de aplicaciones.
Características de ArrayList
- Acceso rápido: Permite acceder a cualquier elemento en tiempo constante O(1) mediante su índice
- Redimensionamiento automático: Crece dinámicamente cuando se añaden elementos
- Optimizada para acceso: Ideal cuando necesitamos acceder frecuentemente a elementos por su posición
- No sincronizada: No es thread-safe por defecto, lo que la hace más eficiente en aplicaciones de un solo hilo
Creación de un ArrayList
Para utilizar ArrayList, primero debemos importar la clase:
import java.util.ArrayList;
Existen varias formas de crear un ArrayList:
// ArrayList vacío que almacenará cadenas de texto
ArrayList<String> nombres = new ArrayList<>();
// Con capacidad inicial específica (optimización)
ArrayList<Integer> numeros = new ArrayList<>(20);
// A partir de otra colección
ArrayList<String> copiaDeNombres = new ArrayList<>(nombres);
Estructura interna
Internamente, ArrayList utiliza un array para almacenar los elementos. Cuando este array se llena, ArrayList crea automáticamente uno nuevo con mayor capacidad y copia todos los elementos al nuevo array. Este proceso es transparente para el programador.
ArrayList<String> frutas = new ArrayList<>();
// Inicialmente tiene un array interno pequeño
// Al añadir elementos, el array crece según sea necesario
frutas.add("Manzana");
frutas.add("Banana");
frutas.add("Naranja");
// Si el array interno se llena, ArrayList crea uno nuevo más grande
Operaciones básicas
Añadir elementos
ArrayList<String> ciudades = new ArrayList<>();
// Añadir al final (operación más común)
ciudades.add("Madrid");
ciudades.add("Barcelona");
// Añadir en una posición específica
ciudades.add(1, "Valencia"); // Se inserta entre Madrid y Barcelona
Acceder a elementos
ArrayList<String> animales = new ArrayList<>();
animales.add("Perro");
animales.add("Gato");
animales.add("Conejo");
// Acceder por índice
String primerAnimal = animales.get(0); // "Perro"
String ultimoAnimal = animales.get(animales.size() - 1); // "Conejo"
Modificar elementos
ArrayList<Double> precios = new ArrayList<>();
precios.add(19.99);
precios.add(29.99);
precios.add(9.99);
// Modificar un elemento existente
precios.set(1, 24.99); // Cambia 29.99 por 24.99
Eliminar elementos
ArrayList<String> tareas = new ArrayList<>();
tareas.add("Estudiar");
tareas.add("Ejercicio");
tareas.add("Compras");
tareas.add("Ejercicio"); // Elemento duplicado
// Eliminar por índice
tareas.remove(0); // Elimina "Estudiar"
// Eliminar por objeto (primera ocurrencia)
tareas.remove("Ejercicio"); // Elimina el primer "Ejercicio"
// Eliminar todos los elementos
// tareas.clear();
Rendimiento de ArrayList
El rendimiento de ArrayList varía según la operación:
- Acceso por índice (get/set): O(1) - Tiempo constante
- Añadir al final (add): O(1) amortizado - Generalmente rápido, excepto cuando necesita redimensionarse
- Insertar/eliminar en posición específica: O(n) - Costoso para listas grandes, ya que requiere desplazar elementos
- Buscar elemento (contains/indexOf): O(n) - Búsqueda lineal
ArrayList<Integer> numeros = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
numeros.add(i);
}
// Acceso por índice - Muy rápido
int numero = numeros.get(500); // O(1)
// Inserción al inicio - Costoso para listas grandes
numeros.add(0, 999); // O(n) - Debe desplazar 1000 elementos
Recorrido de ArrayList
Existen varias formas eficientes de recorrer un ArrayList:
ArrayList<String> lenguajes = new ArrayList<>();
lenguajes.add("Java");
lenguajes.add("Python");
lenguajes.add("JavaScript");
// 1. Bucle for tradicional con índice
for (int i = 0; i < lenguajes.size(); i++) {
System.out.println(lenguajes.get(i));
}
// 2. Bucle for-each (más limpio y recomendado)
for (String lenguaje : lenguajes) {
System.out.println(lenguaje);
}
// 3. Usando Iterator
Iterator<String> it = lenguajes.iterator();
while (it.hasNext()) {
String lenguaje = it.next();
System.out.println(lenguaje);
}
ArrayList vs Array tradicional
ArrayList ofrece varias ventajas sobre los arrays tradicionales:
- Tamaño dinámico: No es necesario definir el tamaño al inicio
- Métodos útiles: Proporciona métodos para añadir, eliminar, buscar, etc.
- Uso de genéricos: Permite especificar el tipo de datos que contendrá
- Conversión sencilla: Fácil conversión entre ArrayList y arrays
// Array tradicional
String[] arrayNombres = new String[3];
arrayNombres[0] = "Ana";
arrayNombres[1] = "Juan";
arrayNombres[2] = "María";
// Para añadir más elementos, necesitaríamos crear un nuevo array
// ArrayList equivalente
ArrayList<String> listaNombres = new ArrayList<>();
listaNombres.add("Ana");
listaNombres.add("Juan");
listaNombres.add("María");
listaNombres.add("Pedro"); // Podemos seguir añadiendo sin preocuparnos
Conversión entre ArrayList y arrays
// De ArrayList a array
ArrayList<Integer> listaNumeros = new ArrayList<>();
listaNumeros.add(10);
listaNumeros.add(20);
listaNumeros.add(30);
// Conversión a array
Integer[] arrayNumeros = listaNumeros.toArray(new Integer[0]);
// De array a ArrayList
String[] frutas = {"Manzana", "Pera", "Uva"};
ArrayList<String> listaFrutas = new ArrayList<>(Arrays.asList(frutas));
Ejemplo práctico: Gestión de inventario
Veamos un ejemplo de cómo utilizar ArrayList para gestionar un inventario simple:
import java.util.ArrayList;
public class GestionInventario {
public static void main(String[] args) {
// Crear inventario usando ArrayList
ArrayList<String> inventario = new ArrayList<>();
// Añadir productos al inventario
inventario.add("Laptop");
inventario.add("Teléfono");
inventario.add("Tablet");
inventario.add("Auriculares");
// Mostrar inventario inicial
System.out.println("Inventario inicial:");
mostrarInventario(inventario);
// Verificar si un producto existe
String productoBuscado = "Tablet";
if (inventario.contains(productoBuscado)) {
System.out.println("\nEl producto '" + productoBuscado + "' está en el inventario");
System.out.println("Posición: " + inventario.indexOf(productoBuscado));
}
// Actualizar un producto
int posicionTelefono = inventario.indexOf("Teléfono");
if (posicionTelefono != -1) {
inventario.set(posicionTelefono, "Smartphone");
System.out.println("\nProducto actualizado");
}
// Eliminar un producto
boolean eliminado = inventario.remove("Auriculares");
if (eliminado) {
System.out.println("Producto 'Auriculares' eliminado");
}
// Mostrar inventario actualizado
System.out.println("\nInventario actualizado:");
mostrarInventario(inventario);
}
private static void mostrarInventario(ArrayList<String> inventario) {
for (int i = 0; i < inventario.size(); i++) {
System.out.println((i + 1) + ". " + inventario.get(i));
}
}
}
La salida de este programa sería:
Inventario inicial:
1. Laptop
2. Teléfono
3. Tablet
4. Auriculares
El producto 'Tablet' está en el inventario
Posición: 2
Producto actualizado
Producto 'Auriculares' eliminado
Inventario actualizado:
1. Laptop
2. Smartphone
3. Tablet
Consideraciones de uso
- Elección adecuada: ArrayList es ideal cuando necesitamos acceso frecuente por índice y pocas inserciones/eliminaciones en medio de la lista.
- Capacidad inicial: Si conocemos aproximadamente el número de elementos, podemos optimizar el rendimiento especificando una capacidad inicial.
- Tipos primitivos: Para almacenar tipos primitivos, debemos usar sus clases envoltorio (Integer, Double, etc.).
- Sincronización: Si necesitamos thread-safety, podemos usar
Collections.synchronizedList(new ArrayList<>())
.
Operaciones CRUD
Las operaciones CRUD (Create, Read, Update, Delete) representan las cuatro funciones fundamentales para la persistencia de datos. En el contexto de las listas en Java, estas operaciones nos permiten manipular colecciones de elementos de manera estructurada y eficiente.
¿Qué son las operaciones CRUD?
El acrónimo CRUD se refiere a:
- Create (Crear): Añadir nuevos elementos a la colección
- Read (Leer): Consultar o recuperar elementos existentes
- Update (Actualizar): Modificar elementos existentes
- Delete (Eliminar): Quitar elementos de la colección
Estas operaciones constituyen la base para cualquier sistema de gestión de datos, desde aplicaciones simples hasta complejos sistemas de bases de datos.
Implementación de CRUD con ArrayList
Vamos a ver cómo implementar cada una de estas operaciones utilizando la clase ArrayList:
Create (Crear)
Para añadir elementos a un ArrayList, disponemos de varios métodos:
ArrayList<String> estudiantes = new ArrayList<>();
// Añadir un elemento al final de la lista
estudiantes.add("María");
// Añadir un elemento en una posición específica
estudiantes.add(0, "Juan"); // Añade "Juan" al principio
// Añadir múltiples elementos de una vez
estudiantes.addAll(Arrays.asList("Pedro", "Ana", "Luis"));
Cuando necesitamos crear registros con estructura más compleja, normalmente utilizamos objetos:
// Definición de la clase Estudiante
class Estudiante {
private int id;
private String nombre;
private int edad;
public Estudiante(int id, String nombre, int edad) {
this.id = id;
this.nombre = nombre;
this.edad = edad;
}
// Getters y setters
public int getId() { return id; }
public String getNombre() { return nombre; }
public int getEdad() { return edad; }
public void setNombre(String nombre) { this.nombre = nombre; }
public void setEdad(int edad) { this.edad = edad; }
@Override
public String toString() {
return "Estudiante{id=" + id + ", nombre='" + nombre + "', edad=" + edad + "}";
}
}
// Uso con ArrayList
ArrayList<Estudiante> listaEstudiantes = new ArrayList<>();
listaEstudiantes.add(new Estudiante(1, "Carlos", 20));
listaEstudiantes.add(new Estudiante(2, "Laura", 22));
Read (Leer)
La operación de lectura nos permite recuperar información de la lista:
ArrayList<String> productos = new ArrayList<>();
productos.add("Laptop");
productos.add("Teléfono");
productos.add("Tablet");
// Leer un elemento específico por su índice
String producto = productos.get(1); // "Teléfono"
// Verificar si un elemento existe
boolean existe = productos.contains("Laptop"); // true
// Encontrar la posición de un elemento
int posicion = productos.indexOf("Tablet"); // 2
int ultimaPosicion = productos.lastIndexOf("Teléfono"); // Útil si hay duplicados
// Obtener el tamaño de la lista
int cantidad = productos.size(); // 3
Para listas de objetos, a menudo necesitamos buscar por algún criterio específico:
// Buscar un estudiante por ID
public Estudiante buscarPorId(ArrayList<Estudiante> lista, int id) {
for (Estudiante e : lista) {
if (e.getId() == id) {
return e;
}
}
return null; // No encontrado
}
// Buscar estudiantes por edad
public ArrayList<Estudiante> buscarPorEdad(ArrayList<Estudiante> lista, int edad) {
ArrayList<Estudiante> resultado = new ArrayList<>();
for (Estudiante e : lista) {
if (e.getEdad() == edad) {
resultado.add(e);
}
}
return resultado;
}
Update (Actualizar)
Para actualizar elementos en un ArrayList, podemos usar el método set()
o modificar directamente los objetos:
ArrayList<String> ciudades = new ArrayList<>();
ciudades.add("Madrid");
ciudades.add("Barcelona");
ciudades.add("Valencia");
// Actualizar un elemento por su índice
ciudades.set(1, "Sevilla"); // Reemplaza "Barcelona" por "Sevilla"
Con objetos, podemos actualizar sus propiedades:
// Actualizar un estudiante por ID
public boolean actualizarEstudiante(ArrayList<Estudiante> lista, int id,
String nuevoNombre, int nuevaEdad) {
for (Estudiante e : lista) {
if (e.getId() == id) {
e.setNombre(nuevoNombre);
e.setEdad(nuevaEdad);
return true; // Actualización exitosa
}
}
return false; // Estudiante no encontrado
}
// Uso
actualizarEstudiante(listaEstudiantes, 1, "Carlos García", 21);
Delete (Eliminar)
Para eliminar elementos de un ArrayList, disponemos de varios métodos:
ArrayList<String> tareas = new ArrayList<>();
tareas.add("Estudiar Java");
tareas.add("Hacer ejercicio");
tareas.add("Leer libro");
tareas.add("Hacer ejercicio"); // Elemento duplicado
// Eliminar por índice
tareas.remove(0); // Elimina "Estudiar Java"
// Eliminar por objeto (primera ocurrencia)
tareas.remove("Hacer ejercicio"); // Elimina la primera ocurrencia
// Eliminar todos los elementos que cumplan una condición
tareas.removeIf(tarea -> tarea.contains("libro"));
// Eliminar todos los elementos
// tareas.clear();
Para listas de objetos, normalmente eliminamos por algún criterio:
// Eliminar un estudiante por ID
public boolean eliminarEstudiante(ArrayList<Estudiante> lista, int id) {
for (int i = 0; i < lista.size(); i++) {
if (lista.get(i).getId() == id) {
lista.remove(i);
return true; // Eliminación exitosa
}
}
return false; // Estudiante no encontrado
}
// Eliminar estudiantes por edad (elimina todos los que cumplan el criterio)
public int eliminarPorEdad(ArrayList<Estudiante> lista, int edad) {
int contador = 0;
Iterator<Estudiante> iterator = lista.iterator();
while (iterator.hasNext()) {
Estudiante e = iterator.next();
if (e.getEdad() == edad) {
iterator.remove(); // Forma segura de eliminar durante iteración
contador++;
}
}
return contador; // Número de estudiantes eliminados
}
Ejemplo completo: Sistema de gestión de biblioteca
Veamos un ejemplo práctico que implementa todas las operaciones CRUD para gestionar libros en una biblioteca:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Scanner;
class Libro {
private int id;
private String titulo;
private String autor;
private boolean prestado;
public Libro(int id, String titulo, String autor) {
this.id = id;
this.titulo = titulo;
this.autor = autor;
this.prestado = false;
}
// Getters y setters
public int getId() { return id; }
public String getTitulo() { return titulo; }
public String getAutor() { return autor; }
public boolean isPrestado() { return prestado; }
public void setTitulo(String titulo) { this.titulo = titulo; }
public void setAutor(String autor) { this.autor = autor; }
public void setPrestado(boolean prestado) { this.prestado = prestado; }
@Override
public String toString() {
return "Libro #" + id + ": '" + titulo + "' de " + autor +
(prestado ? " (Prestado)" : " (Disponible)");
}
}
public class BibliotecaApp {
private ArrayList<Libro> catalogo;
private int siguienteId;
private Scanner scanner;
public BibliotecaApp() {
catalogo = new ArrayList<>();
siguienteId = 1;
scanner = new Scanner(System.in);
// Añadir algunos libros de ejemplo
agregarLibro("Don Quijote", "Miguel de Cervantes");
agregarLibro("Cien años de soledad", "Gabriel García Márquez");
agregarLibro("El principito", "Antoine de Saint-Exupéry");
}
// CREATE: Agregar un nuevo libro
public void agregarLibro(String titulo, String autor) {
Libro nuevoLibro = new Libro(siguienteId++, titulo, autor);
catalogo.add(nuevoLibro);
System.out.println("Libro agregado: " + nuevoLibro);
}
// READ: Buscar libros
public void mostrarTodos() {
if (catalogo.isEmpty()) {
System.out.println("El catálogo está vacío.");
return;
}
System.out.println("\n=== CATÁLOGO DE LIBROS ===");
for (Libro libro : catalogo) {
System.out.println(libro);
}
}
public Libro buscarPorId(int id) {
for (Libro libro : catalogo) {
if (libro.getId() == id) {
return libro;
}
}
return null;
}
public ArrayList<Libro> buscarPorAutor(String autor) {
ArrayList<Libro> resultado = new ArrayList<>();
for (Libro libro : catalogo) {
if (libro.getAutor().toLowerCase().contains(autor.toLowerCase())) {
resultado.add(libro);
}
}
return resultado;
}
// UPDATE: Actualizar información de un libro
public boolean actualizarLibro(int id, String nuevoTitulo, String nuevoAutor) {
Libro libro = buscarPorId(id);
if (libro != null) {
libro.setTitulo(nuevoTitulo);
libro.setAutor(nuevoAutor);
System.out.println("Libro actualizado: " + libro);
return true;
}
System.out.println("Libro no encontrado.");
return false;
}
// UPDATE: Cambiar estado de préstamo
public boolean cambiarEstadoPrestamo(int id) {
Libro libro = buscarPorId(id);
if (libro != null) {
libro.setPrestado(!libro.isPrestado());
System.out.println("Estado actualizado: " + libro);
return true;
}
System.out.println("Libro no encontrado.");
return false;
}
// DELETE: Eliminar un libro
public boolean eliminarLibro(int id) {
Iterator<Libro> iterator = catalogo.iterator();
while (iterator.hasNext()) {
Libro libro = iterator.next();
if (libro.getId() == id) {
iterator.remove();
System.out.println("Libro eliminado correctamente.");
return true;
}
}
System.out.println("Libro no encontrado.");
return false;
}
// Menú principal
public void mostrarMenu() {
int opcion;
do {
System.out.println("\n=== SISTEMA DE GESTIÓN DE BIBLIOTECA ===");
System.out.println("1. Ver todos los libros");
System.out.println("2. Buscar por ID");
System.out.println("3. Buscar por autor");
System.out.println("4. Añadir nuevo libro");
System.out.println("5. Actualizar libro");
System.out.println("6. Cambiar estado de préstamo");
System.out.println("7. Eliminar libro");
System.out.println("0. Salir");
System.out.print("Seleccione una opción: ");
opcion = scanner.nextInt();
scanner.nextLine(); // Consumir el salto de línea
switch (opcion) {
case 1:
mostrarTodos();
break;
case 2:
System.out.print("Ingrese ID del libro: ");
int id = scanner.nextInt();
scanner.nextLine();
Libro libro = buscarPorId(id);
if (libro != null) {
System.out.println("Libro encontrado: " + libro);
} else {
System.out.println("Libro no encontrado.");
}
break;
case 3:
System.out.print("Ingrese nombre del autor: ");
String autor = scanner.nextLine();
ArrayList<Libro> resultados = buscarPorAutor(autor);
if (resultados.isEmpty()) {
System.out.println("No se encontraron libros de ese autor.");
} else {
System.out.println("Libros encontrados:");
for (Libro l : resultados) {
System.out.println(l);
}
}
break;
case 4:
System.out.print("Título del nuevo libro: ");
String titulo = scanner.nextLine();
System.out.print("Autor del nuevo libro: ");
autor = scanner.nextLine();
agregarLibro(titulo, autor);
break;
case 5:
System.out.print("ID del libro a actualizar: ");
id = scanner.nextInt();
scanner.nextLine();
System.out.print("Nuevo título: ");
String nuevoTitulo = scanner.nextLine();
System.out.print("Nuevo autor: ");
String nuevoAutor = scanner.nextLine();
actualizarLibro(id, nuevoTitulo, nuevoAutor);
break;
case 6:
System.out.print("ID del libro a cambiar estado: ");
id = scanner.nextInt();
scanner.nextLine();
cambiarEstadoPrestamo(id);
break;
case 7:
System.out.print("ID del libro a eliminar: ");
id = scanner.nextInt();
scanner.nextLine();
eliminarLibro(id);
break;
case 0:
System.out.println("¡Hasta pronto!");
break;
default:
System.out.println("Opción no válida.");
}
} while (opcion != 0);
scanner.close();
}
public static void main(String[] args) {
BibliotecaApp biblioteca = new BibliotecaApp();
biblioteca.mostrarMenu();
}
}
Consideraciones importantes para operaciones CRUD
Identificadores únicos: Para sistemas CRUD efectivos, es recomendable asignar identificadores únicos a cada elemento, como hicimos con el campo
id
en los ejemplos.Validación de datos: Antes de realizar operaciones de creación o actualización, es importante validar los datos para mantener la integridad de la colección.
Manejo de errores: Implementa un manejo adecuado de situaciones como elementos no encontrados o datos inválidos.
Iteración segura: Cuando elimines elementos durante una iteración, usa
Iterator.remove()
en lugar deArrayList.remove()
para evitar la excepciónConcurrentModificationException
.Rendimiento: Para colecciones grandes, considera el impacto en rendimiento de las operaciones. Por ejemplo, buscar elementos por criterios puede ser costoso en tiempo de ejecución.
Persistencia: En aplicaciones reales, normalmente querrás guardar los datos en algún medio persistente (archivos, bases de datos) en lugar de mantenerlos solo en memoria.
Patrones comunes en implementaciones CRUD
- Patrón DAO (Data Access Object): Separa la lógica de acceso a datos de la lógica de negocio.
// Ejemplo simplificado de patrón DAO
interface LibroDAO {
void crear(Libro libro);
Libro leerPorId(int id);
List<Libro> leerTodos();
boolean actualizar(Libro libro);
boolean eliminar(int id);
}
class LibroDAOImpl implements LibroDAO {
private ArrayList<Libro> libros = new ArrayList<>();
@Override
public void crear(Libro libro) {
libros.add(libro);
}
@Override
public Libro leerPorId(int id) {
for (Libro libro : libros) {
if (libro.getId() == id) {
return libro;
}
}
return null;
}
// Implementación de los demás métodos...
}
Patrón Repositorio: Similar al DAO pero más orientado al dominio y menos a la persistencia.
Patrón Servicio: Encapsula la lógica de negocio que opera sobre los datos.
class BibliotecaServicio {
private LibroDAO libroDAO;
public BibliotecaServicio(LibroDAO libroDAO) {
this.libroDAO = libroDAO;
}
public void prestarLibro(int id) {
Libro libro = libroDAO.leerPorId(id);
if (libro != null && !libro.isPrestado()) {
libro.setPrestado(true);
libroDAO.actualizar(libro);
} else {
throw new RuntimeException("El libro no está disponible");
}
}
// Más métodos de negocio...
}
Las operaciones CRUD constituyen la base fundamental para trabajar con colecciones de datos en Java. Dominar estas operaciones te permitirá desarrollar aplicaciones robustas que gestionen información de manera eficiente y estructurada.
Otros ejercicios de programación de Java
Evalúa tus conocimientos de esta lección Listas con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.
Streams: match
Gestión de errores y excepciones
CRUD en Java de modelo Customer sobre un ArrayList
Clases abstractas
Listas
Métodos de la clase String
Streams: reduce()
API java.nio 2
Polimorfismo
Pattern Matching
Streams: flatMap()
Llamada y sobrecarga de funciones
Métodos referenciados
Métodos de la clase String
Representación de Fecha
Operadores lógicos
Inferencia de tipos con var
Tipos de datos
Estructuras de iteración
Streams: forEach()
Objetos
Funciones lambda
Uso de Scanner
Tipos de variables
Streams: collect()
Operadores aritméticos
Arrays y matrices
Clases y objetos
Interfaz funcional Consumer
CRUD en Java de modelo Customer sobre un HashMap
Interfaces
Enumeraciones Enums
API Optional
Interfaz funcional Function
Encapsulación
Interfaces
Uso de API Optional
Representación de Hora
Herencia básica
Clases y objetos
Interfaz funcional Supplier
HashMap
Sobrecarga de métodos
Polimorfismo de tiempo de ejecución
OOP en Java
Sobrecarga de métodos
CRUD de productos en Java
Clases sealed
Creación de Streams
Records
Encapsulación
Streams: min max
Herencia
Métodos avanzados de la clase String
Funciones
Polimorfismo de tiempo de compilación
Reto sintaxis Java
Conjuntos
Estructuras de control
Recursión
Excepciones
Herencia avanzada
Estructuras de selección
Uso de interfaces
Operadores
Variables
HashSet
Objeto Scanner
Streams: filter()
Operaciones de Streams
Interfaz funcional Predicate
Streams: sorted()
Configuración de entorno
Uso de variables
Clases
Streams: distinct()
Streams: count()
ArrayList
Mapas
Datos de referencia
Interfaces funcionales
Métodos básicos de la clase String
Tipos de datos
Clases abstractas
Instalación
Funciones
Excepciones
Estructuras de control
Herencia de clases
La clase Scanner
Generics
Streams: map()
Funciones y encapsulamiento
Todas las lecciones de Java
Accede a todas las lecciones de Java y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.
Instalación De Java
Introducción Y Entorno
Configuración De Entorno Java
Introducción Y Entorno
Tipos De Datos
Sintaxis
Variables
Sintaxis
Operadores
Sintaxis
Estructuras De Control
Sintaxis
Funciones
Sintaxis
Recursión
Sintaxis
Arrays Y Matrices
Sintaxis
Excepciones
Programación Orientada A Objetos
Clases Y Objetos
Programación Orientada A Objetos
Encapsulación
Programación Orientada A Objetos
Herencia
Programación Orientada A Objetos
Clases Abstractas
Programación Orientada A Objetos
Interfaces
Programación Orientada A Objetos
Sobrecarga De Métodos
Programación Orientada A Objetos
Polimorfismo
Programación Orientada A Objetos
La Clase Scanner
Programación Orientada A Objetos
Métodos De La Clase String
Programación Orientada A Objetos
Excepciones
Programación Orientada A Objetos
Records
Programación Orientada A Objetos
Pattern Matching
Programación Orientada A Objetos
Inferencia De Tipos Con Var
Programación Orientada A Objetos
Enumeraciones Enums
Programación Orientada A Objetos
Generics
Programación Orientada A Objetos
Clases Sealed
Programación Orientada A Objetos
Listas
Framework Collections
Conjuntos
Framework Collections
Mapas
Framework Collections
Funciones Lambda
Programación Funcional
Interfaz Funcional Consumer
Programación Funcional
Interfaz Funcional Predicate
Programación Funcional
Interfaz Funcional Supplier
Programación Funcional
Interfaz Funcional Function
Programación Funcional
Métodos Referenciados
Programación Funcional
Creación De Streams
Programación Funcional
Operaciones Intermedias Con Streams: Map()
Programación Funcional
Operaciones Intermedias Con Streams: Filter()
Programación Funcional
Operaciones Intermedias Con Streams: Distinct()
Programación Funcional
Operaciones Finales Con Streams: Collect()
Programación Funcional
Operaciones Finales Con Streams: Min Max
Programación Funcional
Operaciones Intermedias Con Streams: Flatmap()
Programación Funcional
Operaciones Intermedias Con Streams: Sorted()
Programación Funcional
Operaciones Finales Con Streams: Reduce()
Programación Funcional
Operaciones Finales Con Streams: Foreach()
Programación Funcional
Operaciones Finales Con Streams: Count()
Programación Funcional
Operaciones Finales Con Streams: Match
Programación Funcional
Api Optional
Programación Funcional
Transformación
Programación Funcional
Reducción Y Acumulación
Programación Funcional
Mapeo
Programación Funcional
Streams Paralelos
Programación Funcional
Agrupación Y Partición
Programación Funcional
Filtrado Y Búsqueda
Programación Funcional
Api Java.nio 2
Entrada Y Salida Io
Fundamentos De Io
Entrada Y Salida Io
Leer Y Escribir Archivos
Entrada Y Salida Io
Httpclient Moderno
Entrada Y Salida Io
Clases De Nio2
Entrada Y Salida Io
Api Java.time
Api Java.time
Localtime
Api Java.time
Localdatetime
Api Java.time
Localdate
Api Java.time
Executorservice
Concurrencia
Virtual Threads (Project Loom)
Concurrencia
Future Y Completablefuture
Concurrencia
Spring Framework
Frameworks Para Java
Micronaut
Frameworks Para Java
Maven
Frameworks Para Java
Gradle
Frameworks Para Java
Lombok Para Java
Frameworks Para Java
Quarkus
Frameworks Para Java
Ecosistema Jakarta Ee De Java
Frameworks Para Java
Introducción A Junit 5
Testing
Certificados de superación de Java
Supera todos los ejercicios de programación del curso de Java y obtén certificados de superación para mejorar tu currículum y tu empleabilidad.
En esta lección
Objetivos de aprendizaje de esta lección
- Comprender la interfaz List y sus características principales.
- Conocer las implementaciones comunes de List, especialmente ArrayList.
- Aprender a crear, modificar, acceder y eliminar elementos en listas.
- Entender el rendimiento y uso adecuado de ArrayList.
- Implementar operaciones CRUD con listas y objetos en Java.