Una librería con libros holográficos hechos de código fuente.

Listas en Java: conceptos, usos, iteradores y buenas prácticas

Introducción a las Listas en Java

En Java, las listas son una de las estructuras de datos más utilizadas. Permiten almacenar colecciones de elementos de manera ordenada y proporcionan métodos para acceder, modificar y manipular estos elementos. Se encuentran en el paquete java.util y forman parte del marco de colecciones de Java (Java Collections Framework). A diferencia de los arrays, las listas pueden cambiar de tamaño dinámicamente.

¿Qué es una Lista?

Una lista es una colección ordenada de elementos que pueden ser duplicados. Las listas son dinámicas, lo que significa que su tamaño puede cambiar durante la ejecución del programa.

Características Principales:

  1. Ordenada: Los elementos tienen una posición específica en la lista.
  2. Indexada: Puedes acceder a los elementos por su índice.
  3. Dinámica: El tamaño de la lista puede crecer o reducirse según sea necesario.
  4. Permite duplicados: Puedes almacenar elementos repetidos.

Interfaces y Clases de Listas en Java

En Java, las listas se implementan mediante dos clases principales:

  1. ArrayList: Una lista basada en un array dinámico. Es rápida para acceder a elementos por índice, pero más lenta para insertar o eliminar elementos en posiciones intermedias.
  2. LinkedList: Una lista basada en una estructura de nodos enlazados. Es más eficiente para insertar o eliminar elementos en cualquier posición, pero más lenta para acceder a elementos por índice.

Ambas clases implementan la interfaz List, que define los métodos comunes para trabajar con listas.

Tipos de Listas en Java

Java proporciona varias implementaciones de listas, cada una con sus propias características:

1. ArrayList

  • Basada en un array redimensionable.
  • Acceso rápido a los elementos (O(1) en promedio).
  • Inserciones y eliminaciones costosas en el medio de la lista (O(n)).
  • Ideal cuando se realizan más operaciones de lectura que de modificación.

Ejemplo de uso de ArrayList

import java.util.ArrayList;
import java.util.List;

public class EjemploArrayList {
    public static void main(String[] args) {
        // Crear una lista de cadenas
        List<String> frutas = new ArrayList<>();

        // Añadir elementos a la lista
        frutas.add("Manzana");
        frutas.add("Banana");
        frutas.add("Naranja");

        // Acceder a un elemento por índice
        System.out.println("Primera fruta: " + frutas.get(0));

        // Modificar un elemento
        frutas.set(1, "Pera");

        // Eliminar un elemento
        frutas.remove(2);

        // Recorrer la lista
        for (String fruta : frutas) {
            System.out.println(fruta);
        }

        // Tamaño de la lista
        System.out.println("Número de frutas: " + frutas.size());
    }
}

Explicación del código:

  • ArrayList<String> crea una lista de cadenas.
  • add() añade elementos a la lista.
  • get() accede a un elemento por su índice.
  • set() modifica un elemento en una posición específica.
  • remove() elimina un elemento por su índice.
  • size() devuelve el número de elementos en la lista.

Puedes ver en ListaArray: manejo dinámico de arrays, como construimos desde cero una clase similar a ArrayList.

2. LinkedList

  • Basada en una lista doblemente enlazada.
  • Inserciones y eliminaciones eficientes en cualquier posición (O(1)).
  • Acceso más lento a elementos específicos (O(n)).
  • Recomendada cuando hay muchas operaciones de inserción y eliminación.

Ejemplo de uso de LinkedList

import java.util.LinkedList;

public class EjemploLinkedList {
    public static void main(String[] args) {
        LinkedList<String> tareas = new LinkedList<>();
        tareas.add("Hacer la compra");
        tareas.addFirst("Desayunar");
        tareas.set(0, "Desayunar en casa");
        tareas.addLast("Estudiar Java");
        System.out.println(tareas);
    }
}

3. Vector

  • Similar a ArrayList, pero sincronizado (seguro para hilos).
  • Menos eficiente en programas de un solo hilo.

4. Stack

  • Implementa una estructura tipo «pila» (LIFO: Last In, First Out).
  • Basado en Vector.

Iteradores en Listas

Un iterador es un objeto que permite recorrer una lista elemento por elemento. Proporciona métodos para verificar si hay más elementos (hasNext()), obtener el siguiente elemento (next()) y eliminar el elemento actual (remove()) de forma segura.

1. Borrado incorrecto

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class BorrarMal {
    public static void main(String[] args) {
        List<String> lista = new ArrayList<>();

        lista.add("Azul");
        lista.add("Verde");
        lista.add("Verde");
        lista.add("Naranja");
        lista.add("Morado");
        lista.add("Gris");

        System.out.println(lista);

        // Al borrar de la lista, la posición del elemento siguiente varía, por lo que se salta un verde
        for (int i = 0; i < lista.size(); i++) {
            if (lista.get(i).equals("Verde")) {
                lista.remove(i);
            }
        }

        // Date cuenta como aparece un verde
        System.out.println(lista);
    }

}

2. Uso de Iterator

Para declarar un iterador, debes darle el mismo tipo de la colección que vas a recorrer:

Iterator&lt;Persona> iterator = personas.iterator();

En el siguiente ejemplo, como la lista es de String, el iterador también:

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class EjemploIterador {
    public static void main(String[] args) {
        List<String> colores = new ArrayList<>();
        colores.add("Rojo");
        colores.add("Verde");
        colores.add("Verde");
        colores.add("Azul");

        // Obtener un iterador --> definimos el iterador para String
        Iterator<String> iterador = colores.iterator();

        // Recorrer la lista con el iterador
        while (iterador.hasNext()) {
            String color = iterador.next();
            System.out.println(color);

            // Eliminar el elemento "Verde"
            if (color.equals("Verde")) {
                iterador.remove();
            }
        }

        // Mostrar la lista después de la eliminación
        System.out.println("Colores después de eliminar 'Verde': " + colores);
    }
}

Explicación del código:

  • iterator() obtiene un iterador para la lista. Recuerda que debes declarar el iterador de la misma clase que la lista.
  • hasNext() verifica si hay más elementos.
  • next() devuelve el siguiente elemento.
  • remove() elimina el elemento actual.

3. Uso de ListIterator

  • Permite recorrer en ambas direcciones.
  • Puede modificar elementos.
import java.util.ArrayList;
import java.util.ListIterator;
import java.util.List;

public class ListIteratorExample {

    public static void main(String[] args) {
        List<String> frutas = new ArrayList<>();
        frutas.add("Manzana");
        frutas.add("Plátano");
        frutas.add("Naranja");

        ListIterator<String> listIterator = frutas.listIterator();
        while (listIterator.hasNext()) {
            String fruta = listIterator.next();
            System.out.println(fruta);

            // Insertamos una nueva fruta antes del elemento actual
            if (fruta.equals("Plátano")) {
                listIterator.add("Uva");
            }
        }
    }
}

Este código hace uso de un iterador, como el ejemplo anterior, pero en este caso usa ListIterator para insertar una nueva fruta («Uva») antes del elemento «Plátano».

Buenas Prácticas

  1. Usa la interfaz List para declarar variables: Esto permite cambiar la implementación (ArrayListLinkedList) sin modificar el código.
List<String> lista = new ArrayList<>();
  1. Considera el rendimiento eligiendo la estructura adecuada:
    • Si se realizan inserciones y eliminaciones frecuentemente, usar LinkedList.
    • Si se accede frecuentemente a los elementos, o se requiere un acceso rápido por índice, usar ArrayList.
  2. Evitar iteraciones innecesarias:
    • Utilizar for-each cuando sea posible.
    • Usar Iterator solo cuando se necesiten eliminar elementos mientras se recorre la lista.
  3. Usar ListIterator cuando sea necesario modificar elementos:
    • Permite recorrer en ambas direcciones y modificar elementos en el recorrido.

Ejercicios Propuestos

  1. Crea una lista de números enteros y calcula la suma de todos los elementos.
  2. Implementa una lista de objetos Persona (con nombre y edad) y ordénala por edad.
  3. Usa un iterador para eliminar todos los elementos de una lista que cumplan una condición.

Conclusión

Las listas son una herramienta esencial en Java para manejar colecciones de datos dinámicas. Con ArrayList y LinkedList, puedes elegir la implementación que mejor se adapte a tus necesidades. Los iteradores proporcionan una forma segura y eficiente de recorrer y manipular listas. Siguiendo buenas prácticas, podrás escribir código más limpio, eficiente y mantenible.


Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.