Qual è la differenza tra List.of e Arrays.asList?


117

Java 9 ha introdotto nuovi metodi di fabbrica per gli elenchi, List.of:

List<String> strings = List.of("first", "second");

Qual è la differenza tra la precedente e la nuova opzione? Cioè, qual è la differenza tra questo:

Arrays.asList(1, 2, 3);

e questo:

List.of(1, 2, 3);

1
Vedi anche questo discorso di Stuart "Beaker" Marks.
user1803551

20
@ user1803551 Sebbene comprenda la tua frustrazione, questo ragionamento potrebbe costituire un precedente davvero indesiderato. Molte domande qui hanno una risposta che è "chiaramente dichiarata" (a seconda di come si definisce questo). Ti esorto a portare questa discussione al meta, ma sono abbastanza sicuro che una discussione del genere dovrebbe già esistere (e spero che qualcuno possa trovarla e collegarla :-)
Dimitris Fasarakis Hilliard

4
@ user1803551 Javadocs non menziona la differenza tra i dettagli di implementazione di questi due metodi (come il consumo di spazio o le prestazioni). Penso che anche le persone vorrebbero conoscere questi dettagli.
ZhekaKozlov

5
@ZhekaKozlov Nemmeno la risposta accettata e super votata. Cosa ti dice sugli standard accettati? Ha anche meno informazioni rispetto ai documenti (serializzazione, identità, ordinamento). Se non altro, invia una richiesta a OpenJDK per aggiungere quelle informazioni.
user1803551

3
Questa domanda è in discussione su meta .
Dimitris Fasarakis Hilliard

Risposte:


172

Arrays.asListrestituisce un elenco modificabile mentre l'elenco restituito da List.ofè immutabile :

List<Integer> list = Arrays.asList(1, 2, null);
list.set(1, 10); // OK

List<Integer> list = List.of(1, 2, 3);
list.set(1, 10); // Fails with UnsupportedOperationException

Arrays.asListconsente elementi nulli mentre List.ofno:

List<Integer> list = Arrays.asList(1, 2, null); // OK
List<Integer> list = List.of(1, 2, null); // Fails with NullPointerException

contains si comporta diversamente con i null:

List<Integer> list = Arrays.asList(1, 2, 3);
list.contains(null); // Returns false

List<Integer> list = List.of(1, 2, 3);
list.contains(null); // Fails with NullPointerException

Arrays.asListrestituisce una vista dell'array passato, quindi anche le modifiche all'array si rifletteranno nell'elenco. Per List.ofquesto non è vero:

Integer[] array = {1,2,3};
List<Integer> list = Arrays.asList(array);
array[1] = 10;
System.out.println(list); // Prints [1, 10, 3]

Integer[] array = {1,2,3};
List<Integer> list = List.of(array);
array[1] = 10;
System.out.println(list); // Prints [1, 2, 3]

22
Il fatto che una lista si comporti in modo diverso in base a come è costruita non mi sembra molto orientata agli oggetti. Forse se List.of restituisse un tipo ImmutableList, questo avrebbe senso. Questa è un'astrazione molto permeabile qui.
Sandy Chapman

5
Non sono uno sviluppatore Java, quindi prendila come un'osservazione casuale. C'è forse una buona ragione per cui il comportamento differisce, ma se avessi un metodo che restituisce un List <Integer> come l'esempio, l'interfaccia non sarebbe sufficiente per me sapere se otterrò un'eccezione di runtime se la controllo per null. Allo stesso modo, una modifica nell'implementazione dei metodi potrebbe influenzare il codice distante dal sito di chiamata del mio metodo se il controllo avviene altrove. @Nicolai
Sandy Chapman

8
@SandyChapman questo potrebbe essere un comportamento imprevisto per alcuni (o per la maggior parte?), Ma è un comportamento documentato. Dal List.contains(Object o)javadoc di : "Genera [...] NullPointerException - se l'elemento specificato è nullo e questo elenco non consente elementi nulli (opzionale)". O dalla lunga introduzione dell'interfaccia che pochi leggono: "Alcune implementazioni di raccolte hanno restrizioni sugli elementi che possono contenere"
Aaron

11
@Aaron beh almeno è un'astrazione leaky ben documentata :)
Sandy Chapman

6
Chapman @Sandy: List.of non tornare un ImmutableListtipo, il suo nome reale è solo un dettaglio di implementazione non pubblico. Se era pubblico e qualcuno lo lanciava di Listnuovo, dov'era la differenza? Dov'è la differenza con Arrays.asList, che restituisce Listun'implementazione non pubblica , che genera un'eccezione quando si tenta addo remove, o l'elenco restituito da Collections.unmodifiableListcui non consente alcuna modifica? Riguarda i contratti specificati Listnell'interfaccia. Le interfacce delle collezioni con metodi opzionali erano sempre OOP impure da Java 1.2 ...
Holger

31

Le differenze tra Arrays.asListeList.of

Vedi i JavaDocs e questo discorso di Stuart Marks (o versioni precedenti di esso).

Userò quanto segue per gli esempi di codice:

List<Integer> listOf = List.of(...);
List<Integer> asList = Arrays.asList(...);
List<Integer> unmodif = Collections.unmodifiableList(asList);

Immutabilità strutturale (o: immodificabilità)

Qualsiasi tentativo di cambiare strutturalmenteList.of si tradurrà in un UnsupportedOperationException. Ciò include operazioni come aggiungere , impostare e rimuovere . È tuttavia possibile modificare il contenuto degli oggetti nell'elenco (se gli oggetti non sono immutabili), quindi l'elenco non è "completamente immutabile".

Questo è lo stesso destino per gli elenchi non modificabili creati con Collections.unmodifiableList. Solo questo elenco è una visualizzazione dell'elenco originale, quindi può cambiare se si modifica l'elenco originale.

Arrays.asListnon è completamente immutabile, non ha limitazioni set.

listOf.set(1, "a");  // UnsupportedOperationException
unmodif.set(1, "a"); // UnsupportedOperationException
asList.set(1, "a");  // modified unmodif! unmodif is not truly unmodifiable

Allo stesso modo, cambiando l'array di supporto (se lo tieni premuto) cambierà l'elenco.

L'immutabilità strutturale presenta molti effetti collaterali relativi alla codifica difensiva, alla concorrenza e alla sicurezza che esulano dallo scopo di questa risposta.

Nulla ostilità

List.ofe qualsiasi raccolta a partire da Java 1.5 non consente nullcome elemento. Il tentativo di passare nullcome elemento o anche una ricerca risulterà in un file NullPointerException.

Poiché Arrays.asListè una raccolta da 1.2 (il framework delle raccolte), consente nulls.

listOf.contains(null);  // NullPointerException
unmodif.contains(null); // allowed
asList.contains(null);  // allowed

Modulo serializzato

Poiché List.ofè stato introdotto in Java 9 e gli elenchi creati con questo metodo hanno il loro formato serializzato (binario), non possono essere deserializzati nelle versioni precedenti di JDK (nessuna compatibilità binaria ). Tuttavia, puoi de / serializzare con JSON, ad esempio.

Identità

Arrays.asListinternamente chiama new ArrayList, che garantisce la disuguaglianza di riferimento.

List.ofdipende dall'implementazione interna. Le istanze restituite possono avere l'uguaglianza dei riferimenti, ma poiché ciò non è garantito non puoi fare affidamento su di essa.

asList1 == asList2; // false
listOf1 == listOf2; // true or false

Vale la pena ricordare che le liste sono uguali (tramite List.equals) se contengono gli stessi elementi nello stesso ordine, indipendentemente da come sono state create o da quali operazioni supportano.

asList.equals(listOf); // true i.f.f. same elements in same order

Implementazione (avviso: i dettagli possono cambiare rispetto alle versioni)

Se il numero di elementi nell'elenco di List.ofè 2 o inferiore, gli elementi vengono memorizzati in campi di una classe specializzata (interna). Un esempio è l'elenco che memorizza 2 elementi (sorgente parziale):

static final class List2<E> extends AbstractImmutableList<E> {
    private final E e0;
    private final E e1;

    List2(E e0, E e1) {
        this.e0 = Objects.requireNonNull(e0);
        this.e1 = Objects.requireNonNull(e1);
    }
}

Altrimenti vengono archiviati in una matrice in modo simile a Arrays.asList.

Efficienza nel tempo e nello spazio

Le List.ofimplementazioni basate sul campo (dimensione <2) sono leggermente più veloci su alcune operazioni. Ad esempio, size()può restituire una costante senza recuperare la lunghezza dell'array e contains(E e)non richiede un overhead di iterazione.

Anche la costruzione di un elenco non modificabile tramite List.ofè più veloce. Confronta il costruttore precedente con 2 assegnazioni di riferimento (e anche quella per quantità arbitrarie di elementi) con

Collections.unmodifiableList(Arrays.asList(...));

che crea 2 liste più altre spese generali. In termini di spazio, risparmi il UnmodifiableListwrapper più qualche centesimo. In definitiva, i risparmi HashSetnell'equivalente sono più convincenti.


Tempo di conclusione: usa List.ofquando vuoi un elenco che non cambia e Arrays.asListquando vuoi un elenco che può cambiare (come mostrato sopra).


1
Per le persone che si chiedono perché esiste questa risposta, vedere questo .
user1803551

3
Arrays.asListnon è completamente modificabile. asList.add(1);lancia un UnsupportedOperationException.
mapeters

"Null ostile" è un ottimo modo per dirlo. Praticamente non posso usare il List.oftempo che le persone potrebbero voler chiamare containse non essere sorpreso da una NullPointerException.
Noumenon,

14

Riassumiamo le differenze tra List.of e Arrays.asList

  1. List.ofpuò essere utilizzato al meglio quando il set di dati è inferiore e invariato, mentre Arrays.asListpuò essere utilizzato al meglio in caso di set di dati ampio e dinamico.

  2. List.ofoccupa molto meno spazio di overhead perché ha un'implementazione basata sul campo e consuma meno spazio di heap, sia in termini di overhead fisso che su base per elemento. mentre Arrays.asListoccupa più spazio in testa perché durante l'inizializzazione crea più oggetti nell'heap.

  3. La raccolta restituita da List.ofè immutabile e quindi thread-safe mentre la raccolta restituita da Arrays.asListè modificabile e non thread-safe. (Le istanze di raccolta immutabili generalmente consumano molta meno memoria rispetto alle loro controparti mutabili.)

  4. List.ofnon consente elementi nulli mentre Arrays.asListconsente elementi nulli .


2
"Le istanze di raccolta immutabili generalmente consumano molta meno memoria rispetto alle loro controparti modificabili". - Veramente? Ti andrebbe di approfondire un po 'questo - intendi perché possono essere condivisi in modo sicuro, o intendi che le istanze stesse possono essere implementate in modo più efficiente in qualche modo?
Hulk

1
@ Hulk Il rispondente ha ragione sull'efficienza dello spazio. Guarda il discorso di Stuart Marks: youtu.be/q6zF3vf114M?t=49m48s
ZhekaKozlov

2
@ZhekaKozlov Ciò sembra essere vero in generale, ma sono molto scettico sul fatto che sia vero quando si parla di Arrays.asListversus List.of, dato che il primo è letteralmente solo un wrapper attorno a un array. Almeno l'implementazione di OpenJDK sembra avere un sovraccarico estremamente ridotto. In effetti, List.ofsarebbe necessario creare copie di qualsiasi array passato, quindi a meno che l'array stesso non venga presto GC, sembrerebbe che List.ofabbia un footprint di memoria significativamente più grande.
Chris Hayes

4
@ChrisHayes Almeno List.of(x)e List.of(x, y)sono più efficienti perché non allocano affatto array
ZhekaKozlov

2
@ Hulk: non dimenticare che i List.ofmetodi non sono necessari per restituire nuovi elenchi ogni volta. Questi elenchi hanno un'identità non specificata, quindi potrebbe esserci memorizzazione nella cache, deduplicazione o scalarizzazione gestite a livello di JVM. Se non in questa versione, forse nella prossima. È consentito dal contratto. Al contrario, Array.asListdipende dall'identità dell'array che stai passando, poiché l'elenco risultante è una vista modificabile sull'array, che riflette tutti i cambiamenti bidirezionali.
Holger

3

Oltre alle risposte precedenti ci sono alcune operazioni su cui entrambi List::ofe Arrays::asListdifferiscono:

+----------------------+---------------+----------+----------------+---------------------+
|      Operations      | SINGLETONLIST | LIST::OF | ARRAYS::ASLIST | JAVA.UTIL.ARRAYLIST |
+----------------------+---------------+----------+----------------+---------------------+
|          add         |             |       |              |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
|        addAll        |             |       |              |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
|         clear        |             |       |              |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
|        remove        |             |       |              |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
|       removeAll      |       ❗️       |        |        ❗️       |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
|       retainAll      |       ❗️       |       |        ❗️        |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
|      replaceAll      |             |       |        ✔️       |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
|          set         |             |       |        ✔️       |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
|         sort         |       ✔️       |        |        ✔️      |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
|  remove on iterator  |             |       |              |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
| set on list-iterator |             |       |        ✔️       |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
  1. ✔️ significa che il metodo è supportato
  2. ❌ significa che la chiamata a questo metodo genererà un'eccezione UnsupportedOperationException
  3. ❗️ significa che il metodo è supportato solo se gli argomenti del metodo non causano una mutazione, ad es. Collections.singletonList ("foo"). KeepAll ("foo") è OK ma Collections.singletonList ("foo"). KeepAll ("bar" ) genera un'eccezione UnsupportedOperationException

Ulteriori informazioni sulle collezioni :: singletonList vs. Lista di


1
risposta per l'esame Java
povis
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.