Introduzione alle Liste

Le liste in Java sono fondamentali strutture dati che consentono di memorizzare, gestire e manipolare collezioni di oggetti in modo ordinato e flessibile. A differenza degli array, che hanno una dimensione fissa, le liste possono crescere e ridursi dinamicamente. Questo le rende ideali per situazioni in cui la quantità di dati da gestire può variare nel tempo. Java offre diverse implementazioni di liste, come

  • ArrayList di java.util
  • LinkedList di java.util
  • Implementazione manuale mediante una “Relazione tra classi”

ciascuna con proprie caratteristiche e vantaggi specifici. Comprendere come utilizzare efficacemente le liste è essenziale per scrivere codice Java robusto e efficiente.

Confronto tra Array e Liste

In Java, sia le liste che gli array sono utilizzati per memorizzare collezioni di elementi, ma presentano differenze fondamentali:

VantaggiSvantaggi
Array->Semplicità di gestione.

->Accesso diretto agli elementi.
->Gestione statica del numero degli elementi.

->Difficoltà delle operazioni d’inserimento e cancellazione di elementi con criteri di ordinamento.
Liste->Gestione dinamica del numero degli elementi.

->Facilità nelle operazioni d’inserimento e cancellazione di elementi anche in presenza di criteri di ordinamento.
->Maggiore complessità di gestione rispetto all’array.

->Struttura ad accesso strettamente sequenziale.

Tipi di implementazione: Le Classi del package java.util

Il collections framework di Java ci offre varie implementazioni delle liste, più in particolare sono note le Classi ArrayList e LinkedList che ci permettono di implementare una Lista, seguono le differenze tra le due classi:

  1. ArrayList:
    • Utilizza un array dinamico per memorizzare gli elementi.
    • Gli elementi possono essere visionati direttamente tramite l’indice.
    • È efficiente per l’accesso casuale e l’iterazione sequenziale.
    • L’aggiunta o la rimozione di elementi in posizioni intermedie può richiedere la ridimensionamento dell’array, il che può essere costoso in termini di prestazioni.
    • È più adatto per le situazioni in cui l’accesso casuale è frequente e la modifica della lista è meno frequente.
  1. LinkedList:
    • Utilizza una lista collegata per memorizzare gli elementi.
    • Ogni elemento è collegato al successivo tramite un riferimento.
    • È efficiente per l’inserimento e la rimozione in posizioni intermedie.
    • L’accesso casuale è meno efficiente poiché richiede di attraversare la lista dall’inizio.
    • È più adatto per le situazioni in cui l’inserimento e la rimozione in posizioni intermedie sono frequenti.

Di questi due metodi illustrati ne analizzeremo l’implementazione pratica di uno soltanto ovvero il LinkedList.

Implementazione mediante la classe LinkedList

Supponiamo che ci venga commissionata la creazione di un programma che deve gestire la lista delle persone invitate ad un evento, per ogni persona verranno salvati i dati anagrafici principali (nome, cognome, età), di seguito il Diagramma UML

La classe Persona e la MainClass sono associate in quanto nella MainClass troviamo l’istanziazione di oggetti della classe Persona, quindi la MainClass e la Classe Persona comunicano tra di loro.

Spiegazione della relazione tra le due classi

Nella classe Persona troviamo la definizione di tre Attributi:

  • nome di tipo String
  • cognome di tipo String
  • eta di tipo int

Ovviamente gli attributi sono private in quanto si rispetta la proprietà dell’Incapsulamento delle OOP quindi vengono implementati tutti i setter e getter necessari. Segue il codice della classe persona:

Adesso riportiamo il codice della MainClass dove andremo ad utilizzare la classe LinkedList:

package pacchetto;
import java.util.LinkedList;


public class MainClass {

	public static void main(String[] args) {
		//Dichiarazione della lista mediante la classe 'LinkedList' 
		LinkedList<Persona> lista = new LinkedList<Persona>();
		
		lista.add(new Persona("mario","Lazzaro",18));
		lista.add(new Persona("mario","Rossi",28));
		lista.add(new Persona("Pasquale Pio","De Vita",17));

		System.out.println("Stampo l'ultimo nodo della lista:");
		lista.getLast().printAll();

		System.out.println("Stampo il primo nodo:");
		lista.getFirst().printAll();

		System.out.println("Stampo il secondo nodo nella lista:");
		lista.get(1).printAll();

		System.out.println("\n \nStampo tutti i nodi della lista:");
		for(int i=0; i<lista.size();i++) {
			lista.get(i).printAll();
		}
		
		
	}

}

Nel main() abbiamo dichiarato la nostra lista mediante l’utilizzo della classe LinkedList che segue la seguente sintassi:

LinkedList<Classe_del_dato_da_gestire> nome_lista = new LinkedList<Classe_del_dato_da_gestire>()

Dal JavaDoc della classe ricaviamo i metodi che possiamo applicare alla lista, segue la tabella riassuntiva dei metodi disponibili:

MethodDescription
add(int index, E element)This method Inserts the specified element at the specified position in this list.
add(E e)This method Appends the specified element to the end of this list.
addFirst(E e)This method Inserts the specified element at the beginning of this list.
addLast(E e)This method Appends the specified element to the end of this list.
clear()This method removes all of the elements from this list.
clone()This method returns a shallow copy of this LinkedList.
contains(Object o)This method returns true if this list contains the specified element.
get(int index)This method returns the element at the specified position in this list.
getFirst()This method returns the first element in this list.
getLast()This method returns the last element in this list.
indexOf(Object o)This method returns the index of the first occurrence of the specified element in this list, or -1 if this list does not contain the element.
lastIndexOf(Object o)This method returns the index of the last occurrence of the specified element in this list, or -1 if this list does not contain the element.
offerFirst(E e)This method Inserts the specified element at the front of this list.
offerLast(E e)This method Inserts the specified element at the end of this list.
remove()This method retrieves and removes the head (first element) of this list.
remove(int index)This method removes the element at the specified position in this list.
remove(Object o)This method removes the first occurrence of the specified element from this list if it is present.
removeFirst()This method removes and returns the first element from this list.
removeFirstOccurrence(Object o)This method removes the first occurrence of the specified element in this list (when traversing the list from head to tail).
removeLast()This method removes and returns the last element from this list.
set(int index, E element)This method replaces the element at the specified position in this list with the specified element.
size()This method returns the number of elements in this list.
toArray()This method returns an array containing all of the elements in this list in proper sequence (from first to last element).
toString()This method returns a string containing all of the elements in this list in proper sequence (from first to the last element), each element is separated by commas and the String is enclosed in square brackets.

Implementazione manuale mediante la classe "La Relazioni tra classi"

Adesso analizziamo l’implementazione manuale delle liste in Java mediante una Relazione tra Classi che sfrutta 3 classi:

  • Classe Lista (la classe che in cui verrà implementata la logica di funzionamento della lista )
  • Classe Nodo (la classe che in cui verrà implementata la definizione del Nodo )
  • Classe Dato (la classe che in cui verrà implementata la Classe del Dato da gestire )

Supponiamo che ci venga commissionato lo sviluppo di un applicativo per la gestione dell’anagrafica di un comune in cui per ogni persona venga registrato nome, cognome ed età. Analizziamo il problema e riportiamo di seguito il Diagramma UML :

Da come possiamo vedere troviamo 4 classi, per la gestione della lista si evidenziano le seguenti 3 classi:

  • Anagrafica (Classe corrispondente alla Classe Lista)
  • Nodo (Classe corrispondente alla Classe Nodo)
  • Persona (Classe corrispondente alla Classe Dato)

La Classe Anagrafica contiene tutti i metodi per la visita,inserimento ed eliminazione di un Nodo nella lista; La Classe dipende dalla Classe Persona ed è un’aggregazione della Classe Nodo.

La Classe Persona si occupa di definire l’oggetto da gestire nella lista (in questo caso una persona con nome, cognome ed età), essa compone la Classe Nodo

La Classe Nodo si occupa di definire il nodo della lista, essa composta dalla Classe Persona ed aggregante della Classe Anagrafica inoltre è un’aggregazione di se stessa.

La Classe MainClass contiene il main() e si occupa della gestione del menu() della consolle, inoltre si Occupa della chiamata dei metodi della classe Anagrafica e del istanziazione degli oggetti della Classe Persona da passare ai metodi di inserimento della Classe Anagrafica, quindi è associata alle Classi Anagrafica e Persona

segue il codice della Classe Persona

package pacchetto;

/**
 * Classe per la gestione dei dati anagrafici di una persona
 * @author Mario Lazzaro, Aurelio Costigliola
 */
public class Persona {
	private String nome;
	private String cognome;
	private int eta;
	/**
	 * Costruttore con parametri
	 * @param nome Nome della persona
	 * @param cognome Cognome della persona
	 * @param eta Età della persona
	 */
	public Persona(String nome, String cognome, int eta) {
		this.nome = nome;
		this.cognome = cognome;
		this.eta = eta;
	}
	/**
	 * Costruttore copia
	 * @param oggetto Oggetto della Classe Persona da copiare
	 */
	public Persona(Persona oggetto) {
		this.nome = oggetto.getNome();
		this.cognome=oggetto.getCognome();
		this.eta=oggetto.getEta();
	}
	//getter
	public String getNome() {
		return nome;
	}
	public String getCognome() {
		return cognome;
	}
	public int getEta() {
		return eta;
	}
	//setter
    public void setNome(String nome){
        this.nome = nome;
    }
    public void setCognome(String cognome){
        this.cognome = cognome;
    }
	public void setEta(int eta){
		this.eta=eta;
	}

	/**
	 * Metodo per la stampa a schermo di tutti gli attributi dell'oggetto
	 */
	public void printAll() {
		System.out.println("----------------");
		System.out.println("Nome : "+nome);
		System.out.println("Cognome : "+cognome);
		System.out.println("età : "+eta);
		System.out.println("----------------");
	}
}

Come possiamo vedere la Classe Persona è analoga alla Classe Persona implementata mediante la classe LinkedList, segue quindi il codice della Classe Nodo:


package pacchetto;

/**
 * Classe per definizione dei Nodi di una lista
 * @author Mario Lazzaro, Aurelio Costigliola
 */
public class Nodo {
    private Persona info;
	private Nodo link;

    /**
     * Costruttore con parametri del nodo 
     * @param oggetto 
     */
    
	public Nodo(Persona oggetto) {
		info = new Persona(oggetto);
		link = null;
	}

	//getter

	public Nodo getNodo(){
		return new Nodo(this.getInfo());
	}
	public Persona getInfo() {
		return new Persona(info);
		
	}
	public  Nodo getLink() {
		return link;
	}

	//setter

	public void setLink(Nodo link) {
		this.link = link;
	}
    public void setInfo(Persona info) {
        this.info = info;
    }
}

Ogni Nodo avrà due Attributi:

  • info di tipo Persona che conterrà l’informazione (in questo caso l’oggetto della classe persona)
  • link di tipo Nodo (che conterrà la reference del nodo successivo)
Rappresentazione grafica di vari nodi in una lista

Segue quindi il codice della classe anagrafica:

package pacchetto;

import java.util.Scanner;

/**
 * Anagrafica
 * @author Mario Lazzaro, Aurelio Costigliola 
 */



public class Anagrafica {
    private Nodo head;
    private int elementi; 
    /**
     *Costruttore della classe anagrafica 
     */
    public Anagrafica(){
        head = null;
        elementi=0;   
    }
    //metodi utili
    Scanner input = new Scanner(System.in);
    private void sysPause(){
        System.out.println("Premere un tasto per continuare...");
        input.nextLine();
    }
    private static void clearScreen() {  
        System.out.print("\033[H\033[2J");  
        System.out.flush();  
    }  

    /**
     * Mostra tutta l'Anagrafica
     */
    public void mostaAnagrafica(){
        System.out.flush();
        Nodo p = head;

        while(p != null){  
            p.getInfo().printAll(); 
            p = p.getLink(); 
        }
        sysPause();
    }

    /**
     * Mostra tutte le persone in base al nome
     * @param nome nome da fornire
     */
    public void mostaAnagraficabyNome(String nome){
        System.out.flush();
        Nodo nodo=head;

        while(nodo != null){
            if(nodo.getInfo().getNome().equals(nome)){
                nodo.getInfo().printAll();
            }
            nodo=nodo.getLink();
        }

    }

    /**
     * Mostra tutte le persone in base al cognome fornito
     * @param cognome cognome da fornire
     */
    public void mostaAnagraficabyCognome(String cognome){
        System.out.flush();
        Nodo nodo=head;

        while(nodo != null){
            if(nodo.getInfo().getCognome().equals(cognome)){
                nodo.getInfo().printAll();
            }
            nodo=nodo.getLink();
        }
        sysPause();
    }

    /**
     * Mostra tutte le persone in base alla età fornita
     * @param eta età da fornire
     */
    public void mostaAnagraficabyEta(int eta){
        System.out.flush();
        Nodo nodo=head;

        while(nodo != null){
            if(nodo.getInfo().getEta() == eta){
                nodo.getInfo().printAll();
            }
            nodo=nodo.getLink();
        }
        sysPause();
    }

    /**
     * Mostra tutte le persone in base al nome fornito
     * @param iniziale 
     */
    public void mostaAnagraficabyInizialeNome(String iniziale){
        Nodo nodo=head;
        boolean stampato=false;
        clearScreen();
        System.out.println("Procedo alla ricerca e alla stampa delle persone individuate: ");
        while(nodo != null){
            if(nodo.getInfo().getNome().startsWith(iniziale)){
                nodo.getInfo().printAll();
                stampato=true;
            }
            nodo=nodo.getLink();
        }
        if(stampato==false){
            System.err.println("Errore:Nessuna persona trovata");
        }

        sysPause();
    }

    /**
     * Inserisce in testa un nodo
     * @param persona 
    */
    public void inserisciInTestaPersona(Persona persona){
        Nodo n = new Nodo(persona); //creazione di un nodo
        n.setLink(head); //setto il link del nuovo nodo verso l'attuale head
        head = n; //ora che ho settato il link del nuovo nodo, setto il nuovo head a n
        elementi++; //autoincremento la variabile elementi
    }

    /**
     * Inserisce in coda un nodo
     * @param persona
     */
    public void inserisciInCodaPersona(Persona persona){
        Nodo p = head;
        Nodo n = new Nodo(persona);
        if(p==null)
            inserisciInTestaPersona(persona);
        else{
            while (p.getLink()!=null){
                p=p.getLink();
            }
            n.setLink(null);
            p.setLink(n);
            elementi++;//autoincremento la variabile elementi
        }
    }
    /**
     * Eliminatore di nodi in testa
     */
    public void eliminaInTesta(){
        if(head==null){
            return;
        }
        head = head.getLink();
        elementi--;
    }

    /**
     * Eliminatore di nodi in coda
     */
    public void eliminaInCoda(){
        if(head == null)
            return;
        if(head.getLink() == null){
            eliminaInTesta();
        }else{
            Nodo p = head;
            Nodo pp=head.getLink();
            while(pp.getLink() != null){
                p=pp;
                pp=pp.getLink();
            }
            p.setLink(null);
            elementi--;
        }}
    
        /**
         * Eliminatore mediante il cognome della persona
         * @param cognome Cognome della persona da eliminare
         */
        public void eliminaByCognome(String cognome) {
            Nodo nodo = head;
            Nodo[] elementistampati = new Nodo[elementi];
            int counter = 0;
    
            for (int i = 0; i < elementi; i++) {
                if (nodo.getInfo().getCognome().equalsIgnoreCase(cognome)) {
                    elementistampati[counter] = nodo;
                    System.out.println("N:" + counter);
                    nodo.getInfo().printAll();
                    counter++;
                }
                nodo = nodo.getLink();
            }
    
            if (counter == 0) {
                System.out.println("Nessun nodo trovato con il cognome specificato.");
                return;
            }
    
            System.out.println("Inserire il numero del nodo da eliminare:");
            int scelta = input.nextInt();
    
            if (scelta < 0 || scelta >= counter) {
                System.out.println("Scelta non valida.");
                return;
            }
    
            Nodo nodoDaEliminare = elementistampati[scelta];
    
            if (nodoDaEliminare == head) {
                eliminaInTesta();
            } else if (nodoDaEliminare.getLink() == null) {
                eliminaInCoda();
            } else {
                Nodo precedente = head;
                while (precedente.getLink() != nodoDaEliminare) {
                    precedente = precedente.getLink();
                }
                precedente.setLink(nodoDaEliminare.getLink());
                elementi--;
            }
    
            System.out.println("Eliminato: " + nodoDaEliminare.getInfo().getNome());
        }
        }

Seguono i file per la riproduzione del codice trattato in questo “How to”:

di Mario Lazzaro

Studente V ITI B

Related Post