DOMANDA Differenza tra la classe List e la classe ArrayList java

Pubblicità

Hero467

Utente Attivo
Messaggi
692
Reazioni
404
Punteggio
74
Ho consultato blog conosciuti e non, stackoverflow, ho chiesto a ChatGPT e pure alla nuova ai Bard di Google, ma non riesco a capirne la differenza. Che differenza c’è tra un’istanza della classe List e una della classe ArrayList?
 
La differenza è enorme, principalmente la prima è che List è un'interfaccia, mentre ArrayList è una classe che implementa List.

L'interfaccia ti permette di avere delle funzionalità, i metodi, identici nella firma in tutte le implementazioni dell'interfaccia: il metodo add() o il remove() sono uguali (la loro firma) sia in ArrayList che in LinkedList in quanto ciascuna classe che implementa l'interfaccia fornirà una propria implementazione.

Esempio rapido (sono un pò arrugginito, non lo tocco da anni):

Java:
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

class Test {
    public static void printList(List list) {
        for(int i=0; i<list.size(); i++) {
            System.out.println(list.get(i));
        }
    }
    
    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<Integer>();
        LinkedList<Integer> linkedList = new LinkedList<Integer>();
        
        for(int i=0; i<10; i++) {
            arrayList.add(i);
            linkedList.add(i+10);
        }
        
        System.out.println("Print ArrayList values:");
        Test.printList(arrayList);
        System.out.println("Print Linkedlist values:");
        Test.printList(linkedList);   
    }
}

Visto che le classi implementano List, puoi utilizzare l'interfaccia per accedere ai metodi. Probabile che in qualche codice avrai visto assegnare l'istanza di ArrayList, a List per esempio.

Puoi anche guardare il sorgente di List: https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/util/List.java#L141

E quello di ArrayList: https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/util/ArrayList.java#L109
 
La differenza è enorme, principalmente la prima è che List è un'interfaccia, mentre ArrayList è una classe che implementa List.

L'interfaccia ti permette di avere delle funzionalità, i metodi, identici nella firma in tutte le implementazioni dell'interfaccia: il metodo add() o il remove() sono uguali (la loro firma) sia in ArrayList che in LinkedList in quanto ciascuna classe che implementa l'interfaccia fornirà una propria implementazione.

Esempio rapido (sono un pò arrugginito, non lo tocco da anni):

Java:
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

class Test {
    public static void printList(List list) {
        for(int i=0; i<list.size(); i++) {
            System.out.println(list.get(i));
        }
    }
   
    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<Integer>();
        LinkedList<Integer> linkedList = new LinkedList<Integer>();
       
        for(int i=0; i<10; i++) {
            arrayList.add(i);
            linkedList.add(i+10);
        }
       
        System.out.println("Print ArrayList values:");
        Test.printList(arrayList);
        System.out.println("Print Linkedlist values:");
        Test.printList(linkedList);  
    }
}

Visto che le classi implementano List, puoi utilizzare l'interfaccia per accedere ai metodi. Probabile che in qualche codice avrai visto assegnare l'istanza di ArrayList, a List per esempio.

Puoi anche guardare il sorgente di List: https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/util/List.java#L141

E quello di ArrayList: https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/util/ArrayList.java#L109
Ok, il concetto ora mi è un po’ più chiaro. Mi sfuggono due cose però: nel codice di List moltissimi metodi (come add, remove…) sono dichiarati ma non hanno contenuto. Poi sempre questi metodi sono Boolean, perché?
 
Ok, il concetto ora mi è un po’ più chiaro. Mi sfuggono due cose però: nel codice di List moltissimi metodi (come add, remove…) sono dichiarati ma non hanno contenuto. Poi sempre questi metodi sono Boolean, perché?
Essendo un interfaccia, sta alle classi che la implementeranno definire come sviluppare i relativi metodi.

Il fatto che siano Boolean, a mio parere, è perchè il metodo (una volta implementato) dovrà restituire True/False in base al successo/fallimento dell'operazione richiesta.
 
Essendo un interfaccia, sta alle classi che la implementeranno definire come sviluppare i relativi metodi.

Il fatto che siano Boolean, a mio parere, è perchè il metodo (una volta implementato) dovrà restituire True/False in base al successo/fallimento dell'operazione richiesta.
Ma nel codice di ArrayList non c’è nessun riferimento a List. Non viene importato ne utilizzato in questi metodi.

Poi, non poteva semplicemente essere un void con un throw in caso di mancanza dell’argomento?
 
Ma nel codice di ArrayList non c’è nessun riferimento a List. Non viene importato ne utilizzato in questi metodi.

Poi, non poteva semplicemente essere un void con un throw in caso di mancanza dell’argomento?
Direttamente dalla documentazione Oracle (https://docs.oracle.com/javase/8/docs/api/java/util/ArrayList.html)


Java:
public class ArrayList<E>
    extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, Serializable

Quindi nella definizione stessa di ArrayList è esplicitato che implementa l'interfaccia List (oltre ad altre 3).

Relativamente al fatto di essere un void con throw è semplicemente una scelta di programmazione. Chi ha sviluppato List ha ritenuto più importante la stabilità (e quindi non lanciare un errore se l'applicazione fallisce).
Nulla vieta, ovviamente, di fare un throw all'interno dell'implementazione del metodo.
 
Ok, il concetto ora mi è un po’ più chiaro. Mi sfuggono due cose però: nel codice di List moltissimi metodi (come add, remove…) sono dichiarati ma non hanno contenuto.

Ti ha già risposto Tidus, avrei riscritto la medesima cosa più o meno.
Puoi leggere questo breve tutorial di Oracle, penso ti possa chiarire le cose: https://docs.oracle.com/javase/tutorial/java/IandI/createinterface.html

Comunque classi, interfacce e metodi sono una parte importante di Java. Se lo stai studiando ti conviene riguardarle con calma e sperimentare un pò. Poi tieni a mente che spesso vedrai classi che estendono altre classi, ma nel mondo reale si cerca di evitare di usare *troppo* l'ereditarietà.

Ma nel codice di ArrayList non c’è nessun riferimento a List. Non viene importato ne utilizzato in questi metodi.

Come no? In che senso? Viene implementato dalla classe a questa riga https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/util/ArrayList.java#L109

Questo è ciò che rende ArrayList obbligata a definire i metodi. Se non lo fa di tutti, è perchè eredita una classe che al suo interno fornirà un'implementazione.
 
Ti ha già risposto Tidus, avrei riscritto la medesima cosa più o meno.
Puoi leggere questo breve tutorial di Oracle, penso ti possa chiarire le cose: https://docs.oracle.com/javase/tutorial/java/IandI/createinterface.html

Comunque classi, interfacce e metodi sono una parte importante di Java. Se lo stai studiando ti conviene riguardarle con calma e sperimentare un pò. Poi tieni a mente che spesso vedrai classi che estendono altre classi, ma nel mondo reale si cerca di evitare di usare *troppo* l'ereditarietà.



Come no? In che senso? Viene implementato dalla classe a questa riga https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/util/ArrayList.java#L109

Questo è ciò che rende ArrayList obbligata a definire i metodi. Se non lo fa di tutti, è perchè eredita una classe che al suo interno fornirà un'implementazione.
Direttamente dalla documentazione Oracle (https://docs.oracle.com/javase/8/docs/api/java/util/ArrayList.html)


Java:
public class ArrayList<E>
    extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, Serializable

Quindi nella definizione stessa di ArrayList è esplicitato che implementa l'interfaccia List (oltre ad altre 3).

Relativamente al fatto di essere un void con throw è semplicemente una scelta di programmazione. Chi ha sviluppato List ha ritenuto più importante la stabilità (e quindi non lanciare un errore se l'applicazione fallisce).
Nulla vieta, ovviamente, di fare un throw all'interno dell'implementazione del metodo.
si scusate, nella fretta di andare a vedere i metodi mi sono perso quella riga li. Ora mi è chiaro tutto meno che una cosa: che senso ha usare List nel codice?
 
Usare l'interfaccia ti permette di sfruttare il polimorfismo.

Hai un codice specifico da mostrare, per caso? Così vediamo il caso specifico a cui stai pensando.
 
Usare l'interfaccia ti permette di sfruttare il polimorfismo.

Hai un codice specifico da mostrare, per caso? Così vediamo il caso specifico a cui stai pensando.
Java:
private static void run(String source) {
    Scanner scanner = new Scanner(source);
    List<Token> tokens = scanner.scanTokens();
}
Questo snippet l'ho preso dal libro CraftingIntepreters
 
Java:
private static void run(String source) {
    Scanner scanner = new Scanner(source);
    List<Token> tokens = scanner.scanTokens();
}
Questo snippet l'ho preso dal libro CraftingIntepreters

Penso ci siano un paio di ragioni, almeno: la prima è che si tende ad assegnare in base alle operazioni che devono essere eseguite dopo: da un punto di vista logico, se assegni a List, avrai bisogno solo di ciò che offre List. Se assegni a ArrayList hai accesso a tutte le altre operazioni (classe estese + interfacce).

Inoltre ci sono casi, come nei metodi delle librerie, che conviene utilizzare l'interfaccia invece di una classe: supponi di avere una libreria che opera su oggetti di tipo StringBuffer ed hai un metodo che effettua una qualche operazione su una stringa; se utilizzi esplicitamente StringBuffer ti precludi la possibilità di riutilizzare quel metodo, ad esempio, per StringBuilder.

In sostanza riesci ad astrarre, puoi usare come esempio il mio codice sopra: ho usato apposta List per mostrarti che il metodo accetta entrambe le classe, perchè entrambe implementano List. Se avessi usato come parametro ArrayList, non sarebbe stata possibile la chiamata con LinkedList.
 
Penso ci siano un paio di ragioni, almeno: la prima è che si tende ad assegnare in base alle operazioni che devono essere eseguite dopo: da un punto di vista logico, se assegni a List, avrai bisogno solo di ciò che offre List. Se assegni a ArrayList hai accesso a tutte le altre operazioni (classe estese + interfacce).

Inoltre ci sono casi, come nei metodi delle librerie, che conviene utilizzare l'interfaccia invece di una classe: supponi di avere una libreria che opera su oggetti di tipo StringBuffer ed hai un metodo che effettua una qualche operazione su una stringa; se utilizzi esplicitamente StringBuffer ti precludi la possibilità di riutilizzare quel metodo, ad esempio, per StringBuilder.

In sostanza riesci ad astrarre, puoi usare come esempio il mio codice sopra: ho usato apposta List per mostrarti che il metodo accetta entrambe le classe, perchè entrambe implementano List. Se avessi usato come parametro ArrayList, non sarebbe stata possibile la chiamata con LinkedList.
Ok, ora mi è chiaro grazie. L’ultima cosa: non ho trovato il metodo scanTokens nella documentazione di Scanner. Esiste e magari ho sbagliato io qualcosa oppure è esistito e il libro è solo vecchio?
 
Che libro stai seguendo per imparare Java?

Lo Scanner che dici tu non è quello di Java (java.util.Scanner), probabilmente è quello di Crafting Interpreters: https://github.com/munificent/craft...com/craftinginterpreters/lox/Scanner.java#L47
Ah ok, allora semplicemente l’ha messo ma non l’ha ancora implementato nel punto dove sono arrivato io. Mi ha solo messo confusione l’esistenza della classe scanner come classe standard. Questo spiega anche perché ha usato InputReader e BufferReader per prendere l’input e non scanner
 
Pubblicità
Pubblicità
Indietro
Top