Lista Dinámica en Java

Hola a todos, hoy os explicare que son y como programar la estructura de una lista enlazada en Java.

Una lista dinámica es una colección de objetos enlazadas entre si. Es lo que se denomina una estructura dinámica, estas pueden crecer tanto como se quiera.

No se deben confundir con los arrays, estos debemos definirles el tamaño que tendrán al crearlos.

Estas listas esta compuesta por una clase que maneja la lista y otra que contiene la información de los objetos de la lista, llamados nodos.

Nodo

/**
 *
 * @author DiscoDurodeRoer
 * @param <T>
 */
public class Nodo<T> {
   
    private T dato;
    private Nodo<T> siguiente;

    /**
     * Constructor por defecto
     */
    public Nodo(){
        siguiente=null;
     }

    /**
     * Le pasamos un dato al nodo
     * @param p 
     */
    public Nodo(T p){
        siguiente=null;
        dato = p;
    }

    /**
     * Le pasamos un dato y su siguiente nodo al nodo
     * @param t Dato a insertar
     * @param siguiente Su sisguiente nodo
     */
    public Nodo(T t, Nodo<T> siguiente){
        this.siguiente=siguiente;
        dato = t;
    }
    
    public T getDato() {
        return dato;
    }

    public void setDato(T dato) {
        this.dato = dato;
    }

    public Nodo<T> getSiguiente() {
        return siguiente;
    }

    public void setSiguiente(Nodo<T> siguiente) {
        this.siguiente = siguiente;
    }
    
}

Lista dinamica

package ejercicio_ed_8_discoduroderoer;

import java.util.Iterator;


/**
 * Lista Dinamica Version 2.0
 *
 * @author DiscoDurodeRoer
 * @param <T>
 */
public class ListaDinamica<T> implements Iterable<T>{

    //Atributos
    private Nodo<T> primero;
    private Nodo<T> ultimo;
    private int tamanio;

    public ListaDinamica() {
        primero = null;
        ultimo = null;
        tamanio = 0;
    }

    /**
     * Indica si esta la lista vacia o no
     *
     * @return
     */
    public boolean isEmpty() {
        return tamanio == 0;
    }

    /**
     * Devuelve el tamaño de la lista
     *
     * @return
     */
    public int size() {
        return tamanio;
    }

    /**
     * Devuelve el elemento en la posicion indicada
     *
     * @param index
     * @return
     */
    public T get(int index) {

        //si esta vacio o el indice no es correcto, devuelve null
        if (isEmpty() || (index < 0 || index >= size())) {
            return null;
        } else if (index == 0) {
            return getFirst(); //Cojo el primero
        } else if (index == size() - 1) {
            return getLast(); //Cojo el ultimo
        } else {

            //Uso la funcion getNode para coger el nodo deseado
            Nodo<T> buscado = getNode(index);

            return buscado.getDato();

        }

    }

    /**
     * Devuelve el primer elemento, null si esta vacia la lista
     *
     * @return
     */
    public T getFirst() {
        //Si esta vacia, no hay primero que coger
        if (isEmpty()) {
            return null;
        } else {
            return primero.getDato();
        }
    }

    /**
     * Devuelve el ultimo elemento, null si esta vacia la lista
     *
     * @return
     */
    public T getLast() {
        //Si esta vacia, no hay ultimo que coger
        if (isEmpty()) {
            return null;
        } else {
            return ultimo.getDato();
        }
    }

    /**
     * Devuelve el nodo completo de una posicion concreta
     *
     * @param index
     * @return
     */
    private Nodo<T> getNode(int index) {

        //si esta vacio o el indice no es correcto, devuelve null
        if (isEmpty() || (index < 0 || index >= size())) {
            return null;
        } else if (index == 0) {
            return primero; //devuelvo el primero
        } else if (index == size() - 1) {
            return ultimo; //devuelvo el ultimo
        } else {

            Nodo<T> buscado = primero;

            //Busco el nodo deseado con un recorrido
            int contador = 0;
            while (contador < index) {
                contador++;
                buscado = buscado.getSiguiente();
            }

            //Me devuelve el buscado
            return buscado;

        }

    }

    /**
     * Añade un elemento al final de la lista
     *
     * @param elemento
     * @return elemento añadido
     */
    public T addLast(T elemento) {

        Nodo<T> aux;

        //Si esta vacia, hago lo mismo que si fuera añadir el primero
        if (isEmpty()) {
            return addFirst(elemento);
        } else {

            //Hago el intercambio
            aux = new Nodo<>(elemento, null);

            ultimo.setSiguiente(aux);
            ultimo = aux;

        }

        //Aumento el tamanño
        tamanio++;
        return ultimo.getDato();

    }

    /**
     * Añade el elemento al principio de la lista
     *
     * @param elemento
     * @return elemento añadido
     */
    public T addFirst(T elemento) {

        Nodo<T> aux;

        //si esta vacia, el nodo será el primero y ultimo
        if (isEmpty()) {
            aux = new Nodo<>(elemento, null);
            primero = aux;
            ultimo = aux;
        } else {
            //Hago el intercambio
            Nodo<T> primeroActual = primero;
            aux = new Nodo<>(elemento, primeroActual);
            primero = aux;

        }

        //Aumento el tamanño
        tamanio++;
        return primero.getDato();

    }

    /**
     * Añade un elemento en una posicion indicada
     *
     * @param elemento
     * @param index debe ser un indice valido
     * @return elemento añadido
     */
    public T add(T elemento, int index) {

        //si esta vacio o el indice no es correcto, devuelve null
        if (index == 0) {
            return addFirst(elemento); //Añade en la primera posicion
        } else if (index == size()) {
            return addLast(elemento); //añade en la ultima posicion
        } else if ((index < 0 || index >= size())) {
            return null;
        } else {

            //Cojo el anterior nodo al que estoy buscando
            Nodo<T> buscado_anterior = getNode(index - 1);

            //Cojo el nodo de la posicion indicada
            Nodo<T> buscado_actual = getNode(index);

            //Creo el nuevo novo, este apuntara al de la posicion actual
            Nodo<T> aux = new Nodo<>(elemento, buscado_actual);

            //el nodo anterior apunta a nuestro nuevo nodo
            buscado_anterior.setSiguiente(aux);

            //Aumento el tamaño
            tamanio++;
            return getNode(index).getDato();

        }

    }

    /**
     * Devuelve el estado de la lista
     *
     * @return
     */
    public String toString() {

        String contenido = "";

        //Si esta vacia, lo indica
        if (isEmpty()) {
            contenido = "Lista vacia";
        } else {

            Nodo<T> aux = primero;

            //Recorre la lista, mostrando el contenido
            while (aux != null) {
                contenido += aux.toString();
                aux = aux.getSiguiente();
            }

        }

        return contenido;

    }

    /**
     * Indica si existe el elemento indicado
     *
     * @param elemento
     * @return
     */
    public boolean exists(T elemento) {

        //Si esta vacio, devuelve el false
        if (isEmpty()) {
            return false;
        } else {

            Nodo<T> aux = primero;

            //Recorremos la lista
            while (aux != null) {
                if (elemento.equals(aux.getDato())) { //Mejor .equals que ==
                    return true; //Existe
                }
                aux = aux.getSiguiente();
            }

            //Si no lo encuentra devuelve false
            return false;

        }
    }

    /**
     * Indica la posición del elemento
     *
     * @param elemento
     * @return
     */
    public int indexOf(T elemento) {

        //Si esta vacio, devuelvemos -1
        if (isEmpty()) {
            return -1;
        } else {

            Nodo<T> aux = primero;

            int posicion = 0;
            while (aux != null) {
                if (elemento.equals(aux.getDato())) { //Mejor .equals que ==
                    return posicion; //Existe
                }
                posicion++;
                aux = aux.getSiguiente();
            }
            //Si no lo encuentra devuelve -1
            return -1;

        }

    }

    /**
     * Elimina el primer elemento de la lista
     *
     * @return
     */
    public T removeFirst() {

        //Si la lista esta vacia, devolvemos null
        if (isEmpty()) {
            return null;
        } else {

            //Guardo el elemento antes
            T elemento = primero.getDato();

            //Cojo el segundo
            Nodo<T> aux = primero.getSiguiente();
            primero = null; //Lo marco como null para el recolector
            primero = aux; //Este es mi nuevo primero

            //En caso de que borremos el ultimo elemento,el ultimo también
            if (size() == 1) {
                ultimo = null;
            }

            tamanio--;

            return elemento;

        }

    }

    /**
     * Borra el ultimo elemento de la lista
     *
     * @return
     */
    public T removeLast() {

        if (isEmpty()) {
            return null;
        } else {

            //Coge el elemento antes de borrar
            T elemento = ultimo.getDato();

            //Cojo el penultimo
            Nodo<T> aux = getNode(size() - 2);

            //En caso de que sea null
            //Hay 1 o dos elementos
            if (aux == null) {

                //marco el ultimo como nulo
                ultimo = null;
                //Si hay dos, el primero y el ultimo seran el mismo
                //Si hay 1 elemento, significa que borramos la lista
                if (size() == 2) {
                    ultimo = primero;
                } else {
                    primero = null;
                }

            } else {
                //el penultimo es el nuevo ultimo 
                //y le ponemos como siguiente nulo
                ultimo = null;
                ultimo = aux;
                ultimo.setSiguiente(null);
            }

            tamanio--;

            return elemento;

        }

    }

    /**
     * Elimina el nodo de la lista en una posicion concreta
     *
     * @param index
     * @return
     */
    public T remove(int index) {
        //si esta vacio o el indice no es correcto, devuelve null
        if (isEmpty() || (index < 0 || index >= size())) {
            return null;
        } else if (index == 0) {
            return removeFirst();
        } else if (index == size() - 1) {
            return removeLast();
        } else {

            //Cojo el nodo anterior al que quiero borrar
            Nodo<T> nodo_anterior = getNode(index - 1);

            //Cojo el nodo que quiero borrar
            Nodo<T> nodo_actual = getNode(index);

            //Cojo el elemento antes de borrar
            T elemento = nodo_actual.getDato();

            //Cojo el nodo siguiente al que quiero borrar
            Nodo<T> nodo_siguiente = nodo_actual.getSiguiente();

            //Lo marco para borrar para el recolector
            nodo_actual = null;

            //El nodo anterior apunta al nodo siguiente
            nodo_anterior.setSiguiente(nodo_siguiente);

            tamanio--;

            return elemento;

        }
    }

    /**
     * Modifico el elemento de una posicion No afecta a la estructura de la
     * lista
     *
     * @param elemento
     * @param index
     * @return
     */
    public T modify(T elemento, int index) {

        //si esta vacio o el indice no es correcto, devuelve null
        if (isEmpty() || (index < 0 || index >= size())) {
            return null;
        } else {

            //Nodo actual
            Nodo<T> aux = getNode(index);

            //modifico
            aux.setElemento(elemento);

            return aux.getDato();

        }

    }

    @Override
    public Iterator<T> iterator() {
        return new MyIterator();
    }

    //Creo la clase interna MyIterator, que implementa la interfaz Iterator
    private class MyIterator<ListaDinamica> implements Iterator<T>{

        //Indica el siguiente elemento que se va a devolver 
        private int siguiente;
        
        //Indica si hay un elemento
        @Override
        public boolean hasNext() {
            return getNode(siguiente)!=null;
        }

        //Devuelve el elemento actual e incrementa al siguiente
        @Override
        public T next() {
            T elemento = getNode(siguiente).getDato();
            siguiente++;
            return elemento;
        }
        
    }
    

}

Os dejo también un pequeño fichero de prueba para que veáis como funciona.

package ejercicio_ed_8_discoduroderoer;

import java.util.Iterator;

public class Ejercicio_ED_8_Discoduroderoer {

    public static void main(String[] args) {
       
        //Version 1.0
        
        ListaDinamica<Integer> lista = new ListaDinamica<>();
        
        //Añadimos elementos, recordar que devuelve el elemento
        System.out.println("Añadido el elemento "+lista.addLast(1));
        System.out.println("Añadido el elemento "+lista.addLast(2));
        System.out.println("Añadido el elemento "+lista.addLast(3));
        System.out.println("Añadido el elemento "+lista.addFirst(4));
        System.out.println("Añadido el elemento "+lista.addLast(5));
        System.out.println("Añadido el elemento "+lista.addLast(6));
        System.out.println("Añadido el elemento "+lista.add(7, 2));
        System.out.println("Añadido el elemento "+lista.add(8, 4));
        System.out.println("Añadido el elemento "+lista.addFirst(9));
        
        //Orden actual: 9 4 1 7 2 8 3 5 6
        System.out.println("");
        System.out.println("Estado de la lista");
        System.out.println(lista.toString());
        
        //Cojo el valor de la posicion 7 = 5
        System.out.println("Elemento de la posicion 7: "+lista.get(7));
        
        //Cojo el valor de la posicion 9 = null, ya que no existe esa posición
        System.out.println("Elemento de la posicion 9: "+lista.get(9));
        
        //Cojo el valor de la primera posicion = 9
        System.out.println("Elemento de la primera posicion: "+lista.getFirst());
        
        //Cojo el valor de la ultima posicion = 6
        System.out.println("Elemento de la ultima posicion: "+lista.getLast());
        
        //Version 2.0
        
        System.out.println("Borrado el primer elemento "+lista.removeFirst());
        System.out.println("Borrado el elemento de la tercera posicion "+lista.remove(2));
        System.out.println("Borrado el primer elemento "+lista.removeFirst());
        System.out.println("Borrado el elemento de la sexta posicion "+lista.remove(5));
        System.out.println("Borrado el elemento "+lista.removeLast());
        
        //orden actual: 1 2 8 3
        System.out.println("");
        System.out.println("Estado de la lista");
        System.out.println(lista.toString());
        
        //true
        System.out.println("¿Existe el elemento 2? "+lista.exists(2));
        
        //false
        System.out.println("¿Existe el elemento 2? "+lista.exists(10));
        
        //Posicion 1
        System.out.println("Posicion del elemento 2: "+lista.indexOf(2));
        
        //Posicicon -1, no existe
        System.out.println("Posicion del elemento 10: "+lista.indexOf(10));
        
        //Elemento modificado
        lista.modify(10, 0);
        
        //orden actual: 10 2 8 3
        System.out.println("");
        System.out.println("Estado de la lista");
        System.out.println(lista.toString());
        
        System.out.println("Iterable clasico");
        
        Iterator<Integer> it = lista.iterator();
        
        while(it.hasNext()){
            System.out.println(it.next());
        }
        
        System.out.println("Iterable for each");
        
        for(int i:lista){
            System.out.println(i);
        }
        
    }

}


Como vemos, el único atributo de la lista es primero que es el primer elemento, es decir, su referencia, este con su atributo siguiente apuntara al siguiente elemento cuando se añade un nuevo elemento. Puede tener también otros atributos, como ultimo que indicara el ultimo elemento.

Esta lista dinámica puede usarse para cualquier tipo de dato, gracias a los genéricos, fíjate en el fichero de prueba de como se debe iniciar. Una vez hecho, todos los datos son de ese tipo dentro de un nodo

Veamos un pequeño esquema sobre como es una lista enlazada.

Cuando la creamos tendría este aspecto.

listaEnlazada1

Como vemos, no tiene nada, ya que no hemos insertado nada. Cuando insertamos un dato, internamente se crea un nodo, si la lista esta vacía sera el primero, sino lo esta, normalmente, va al final pero según como quieras insertar.

listaEnlazada2

Insertamos otro elemento al final.

listaEnlazada3

Cuando insertamos datos (en este caso 2 elementos), primero apunta al primer nodo y este al segundo. En el último nodo,  el atributo siguiente es null, ya que no hay mas nodos.

También podemos borrar, si borramos el primero, el nodo2 sera el nuevo primero.

listaEnlazada4

Y así consecutivamente, os recomiendo ejecutar el código de prueba e intentar comprender el código.

Para recorrer una lista enlazada que tenga varios nodos, siempre empezamos por el nodo primero y usando el método getSiguiente(), avanzamos por cada uno de los nodos. El código sería así:


Nodo<T> aux=primero; //aux apunta al primero
while(aux!=null){
     //Lo que quieras
     aux=aux.getSiguiente();
}

Gráficamente, se haría así para esta lista enlazada:

listaEnlazada3Aux apunta al primer nodo.

recorridoListaEnlazada1
Después, aux apunta al segundo nodo.
recorridoListaEnlazada2
Por ultimo, aux vale null pero como en la comprobación del while hemos puesto que si aux!=null continue, como es null, se para.

recorridoListaEnlazada3

Os dejo una serie de vídeos donde hemos ido creándola.

Os dejo aquí el repositorio de Github.

Espero que os sea de ayuda. Si teneos dudas, preguntad. Estamos para ayudarte.

Compartir

4 comentarios

  1. Chanito

    la función getElemento() dónde la defines????

  2. jose

    De donde sacaste el metodo getElemento

  3. Falu Sanchez Mera

    Hola, me gustaría saber de donde se obtiene ese «getElemento()» por favor.

  4. Disco Duro de Roer Post author

    Cambialo por getDato().

    Lo modifico, gracias.

Deja una respuesta

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