Quelle est la différence entre List.of et Arrays.asList?

117

Java 9 a introduit de nouvelles méthodes d'usine pour les listes List.of:

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

Quelle est la différence entre l'ancienne et la nouvelle option? Autrement dit, quelle est la différence entre ceci:

Arrays.asList(1, 2, 3);

et ça:

List.of(1, 2, 3);
ZhekaKozlov
la source
1
Voir aussi cette conférence de Stuart "Beaker" Marks.
user1803551
20
@ user1803551 Bien que je comprenne votre frustration, ce raisonnement pourrait créer un précédent vraiment indésirable. Beaucoup de questions ici ont une réponse qui est «clairement énoncée» (selon la façon dont on définit cela). Je vous exhorte à amener cette discussion à la méta mais je suis presque sûr qu'une telle discussion devrait déjà exister (et j'espère que quelqu'un pourra la trouver et la lier :-)
Dimitris Fasarakis Hilliard
4
@ user1803551 Les Javadocs ne mentionnent pas la différence entre les détails d'implémentation de ces deux méthodes (comme la consommation d'espace ou les performances). Je pense que les gens aimeraient aussi connaître ces détails.
ZhekaKozlov
5
@ZhekaKozlov La réponse acceptée et super-votée ne l'est pas non plus. Qu'est-ce que cela vous dit sur les normes acceptées? Il contient même moins d' informations que dans les documents (sérialisation, identité, commande). Si quoi que ce soit, envoyez une demande à OpenJDK pour ajouter ces informations.
user1803551
3
Cette question est discutée sur meta .
Dimitris Fasarakis Hilliard

Réponses:

172

Arrays.asListrenvoie une liste modifiable alors que la liste retournée par List.ofest immuable :

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.asListautorise les éléments nuls alors que List.ofne le fait pas:

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

contains se comporte différemment avec les valeurs nulles:

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.asListrenvoie une vue du tableau passé, de sorte que les modifications apportées au tableau seront également reflétées dans la liste. Car List.ofce n'est pas vrai:

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]
ZhekaKozlov
la source
22
Pour qu'une liste se comporte différemment en fonction de la façon dont elle est construite, cela ne me semble pas très orienté objet. Peut-être que si List.of retournait un type ImmutableList, cela aurait du sens. C'est une abstraction très fuyante ici.
Sandy Chapman
5
Je ne suis pas un développeur Java, alors prenez cela comme une observation occasionnelle. Il y a peut-être une bonne raison pour que le comportement diffère, mais si j'avais une méthode renvoyant un List <Integer> comme l'exemple, l'interface ne me suffirait pas pour savoir si j'obtiendrai une exception d'exécution si je la vérifie pour les valeurs nulles. De même, un changement dans l'implémentation de cette méthode pourrait affecter le code distant du site d'appel de ma méthode si cette vérification se produit ailleurs. @Nicolai
Sandy Chapman
8
@SandyChapman cela peut être un comportement inattendu pour certains (ou la plupart?), Mais il s'agit d'un comportement documenté. Depuis le List.contains(Object o)javadoc du 's : "Lance [...] NullPointerException - si l'élément spécifié est nul et que cette liste n'autorise pas les éléments nuls (facultatif)". Ou de la longue introduction de l'interface que peu de gens lisent: "Certaines implémentations de collection ont des restrictions sur les éléments qu'elles peuvent contenir"
Aaron
11
@Aaron bien au moins, c'est une abstraction qui fuit bien documentée :)
Sandy Chapman
6
@Sandy Chapman: List.of ne revenir un ImmutableListtype de son nom réel est un détail de mise en œuvre non publiques. Si c'était public et que quelqu'un l'a rejoué List, où était la différence? Où est la différence avec Arrays.asList, qui renvoie une Listimplémentation non publique , qui lève une exception lors de la tentative addou remove, ou la liste renvoyée par Collections.unmodifiableListlaquelle ne permet aucune modification? Tout est question de contrats spécifiés dans l' Listinterface. Les interfaces Collections avec des méthodes optionnelles étaient toujours impures POO depuis Java 1.2…
Holger
31

Les différences entre Arrays.asListetList.of

Voir les JavaDocs et cet exposé de Stuart Marks (ou des versions précédentes de celui-ci).

J'utiliserai ce qui suit pour les exemples de code:

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

Immuabilité structurelle (Ou: non modifiable)

Toute tentative de changement structurelList.of entraînera un UnsupportedOperationException. Cela inclut des opérations telles que l' ajout , la définition et la suppression . Vous pouvez cependant modifier le contenu des objets de la liste (si les objets ne sont pas immuables), de sorte que la liste ne soit pas «complètement immuable».

C'est le même sort pour les listes non modifiables créées avec Collections.unmodifiableList. Seule cette liste est une vue de la liste d'origine, elle peut donc changer si vous modifiez la liste d'origine.

Arrays.asListn'est pas complètement immuable, il n'a pas de restriction sur set.

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

De même, changer le tableau de sauvegarde (si vous le maintenez) changera la liste.

L'immuabilité structurelle s'accompagne de nombreux effets secondaires liés au codage défensif, à la concurrence et à la sécurité qui dépassent le cadre de cette réponse.

Nulle hostilité

List.ofet toute collection depuis Java 1.5 ne permet pas nullen tant qu'élément. Tenter de passer nullen tant qu'élément ou même une recherche entraînera un fichier NullPointerException.

Puisqu'il Arrays.asLists'agit d'une collection de 1.2 (le Framework de collections), il autorise nulls.

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

Formulaire sérialisé

Depuis List.ofson introduction dans Java 9 et les listes créées par cette méthode ont leur propre forme sérialisée (binaire), elles ne peuvent pas être désérialisées sur les versions antérieures de JDK (pas de compatibilité binaire ). Cependant, vous pouvez dé / sérialiser avec JSON, par exemple.

Identité

Arrays.asListappels internes new ArrayList, ce qui garantit l'inégalité de référence.

List.ofdépend de la mise en œuvre interne. Les instances renvoyées peuvent avoir une égalité de référence, mais comme cela n'est pas garanti, vous ne pouvez pas vous y fier.

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

Il convient de mentionner que les listes sont égales (via List.equals) si elles contiennent les mêmes éléments dans le même ordre, indépendamment de la façon dont elles ont été créées ou des opérations qu'elles prennent en charge.

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

Mise en œuvre (avertissement: les détails peuvent changer au fil des versions)

Si le nombre d'éléments dans la liste de List.ofest de 2 ou moins, les éléments sont stockés dans des champs d'une classe spécialisée (interne). Un exemple est la liste qui stocke 2 éléments (source partielle):

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);
    }
}

Sinon, ils sont stockés dans un tableau de la même manière que Arrays.asList.

Efficacité temporelle et spatiale

Les List.ofimplémentations basées sur le champ (taille <2) sont légèrement plus rapides sur certaines opérations. Par exemple, size()peut renvoyer une constante sans récupérer la longueur du tableau et contains(E e)ne nécessite pas de surcharge d'itération.

Construire une liste non modifiable via List.ofest également plus rapide. Comparez le constructeur ci-dessus avec 2 affectations de référence (et même celle pour un nombre arbitraire d'éléments) à

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

ce qui crée 2 listes plus d'autres frais généraux. En termes d'espace, vous enregistrez l' UnmodifiableListemballage plus quelques centimes. Au final, les économies en HashSetéquivalent sont plus convaincantes.


Temps de conclusion: à utiliser List.oflorsque vous voulez une liste qui ne change pas et Arrays.asListlorsque vous voulez une liste qui peut changer (comme indiqué ci-dessus).

user1803551
la source
1
Pour les personnes se demandant pourquoi cette réponse existe, voyez ceci .
user1803551
3
Arrays.asListn'est pas entièrement modifiable. asList.add(1);jette un UnsupportedOperationException.
mapeters
"Null hostile" est une excellente façon de le dire. Je ne peux pratiquement pas utiliser le List.oftemps que les gens voudront appeler containset ne pas être surpris par une NullPointerException.
Noumenon le
14

Résumons les différences entre List.of et Arrays.asList

  1. List.ofpeut être mieux utilisé lorsque l'ensemble de données est inférieur et inchangé, alors qu'il Arrays.asListpeut être utilisé au mieux dans le cas d'un ensemble de données volumineux et dynamique.

  2. List.ofprend très moins d'espace supplémentaire car il a une implémentation basée sur le terrain et consomme moins d'espace de tas, à la fois en termes de surcharge fixe et sur une base par élément. tandis que Arrays.asListprennent plus d'espace de surcharge car lors de l'initialisation, il crée plus d'objets dans le tas.

  3. La collection retournée par List.ofest immuable et donc sécurisée pour les threads tandis que la collection retournée par Arrays.asListest mutable et non thread-safe. (Les instances de collection immuables consomment généralement beaucoup moins de mémoire que leurs homologues mutables.)

  4. List.ofne permet pas null éléments tout Arrays.asListpermet nuls éléments.

Mohit Tyagi
la source
2
"Les instances de collection immuables consomment généralement beaucoup moins de mémoire que leurs homologues mutables." - Vraiment? Voudriez-vous élaborer un peu là-dessus - voulez-vous dire parce qu'ils peuvent être partagés en toute sécurité, ou voulez-vous dire que les instances elles-mêmes peuvent être mises en œuvre plus efficacement d'une manière ou d'une autre?
Hulk
1
@Hulk Le répondant a raison sur l'efficacité de l'espace. Voir la conférence de Stuart Marks: youtu.be/q6zF3vf114M?t=49m48s
ZhekaKozlov
2
@ZhekaKozlov Cela semble être vrai en général, mais je suis très sceptique que ce soit vrai quand on parle de Arrays.asListversus List.of, étant donné que le premier n'est littéralement qu'un wrapper autour d'un tableau. Au moins l'implémentation d'OpenJDK semble avoir une surcharge extrêmement faible. En fait, List.ofil faudrait faire des copies de tout tableau passé, donc à moins que le tableau lui-même ne soit bientôt GC, il semblerait qu'il List.ofait une empreinte mémoire beaucoup plus grande.
Chris Hayes
4
@ChrisHayes Au moins List.of(x)et List.of(x, y)sont plus efficaces car ils n'allouent pas du tout de tableaux
ZhekaKozlov
2
@Hulk: n'oubliez pas que les List.ofméthodes ne sont pas obligées de renvoyer de nouvelles listes à chaque fois. Ces listes ont une identité non spécifiée, de sorte qu'une mise en cache, une déduplication ou une scalarisation peuvent être gérées au niveau de la JVM. Si ce n'est pas dans cette version, alors peut-être dans la suivante. C'est permis par le contrat. En revanche, cela Array.asListdépend de l'identité du tableau que vous transmettez, car la liste résultante est une vue mutable sur le tableau, reflétant tout changement bidirectionnel.
Holger
3

En dehors des réponses ci-dessus, il existe certaines opérations sur lesquelles les deux List::ofet Arrays::asListdiffèrent:

+----------------------+---------------+----------+----------------+---------------------+
|      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. ✔️ signifie que la méthode est prise en charge
  2. ❌ signifie que l'appel de cette méthode lèvera une exception UnsupportedOperationException
  3. ❗️ signifie que la méthode n'est prise en charge que si les arguments de la méthode ne provoquent pas de mutation, par exemple Collections.singletonList ("foo"). ) lève une exception UnsupportedOperationException

En savoir plus sur les collections :: singletonList Vs. Liste de

Vishwa Ratna
la source
1
réponse à l'examen java
povis