web-dev-qa-db-fra.com

Arrays.asList () ne fonctionne pas comme il se doit?

J'ai un float [] et j'aimerais avoir une liste avec les mêmes éléments. Je pourrais faire la chose laide de les ajouter un par un, mais je voulais utiliser la méthode Arrays.asList. Il y a cependant un problème. Cela marche:

List<Integer> list = Arrays.asList(1,2,3,4,5);

Mais ce n'est pas le cas.

int[] ints = new int[] {1,2,3,4,5};
List<Integer> list = Arrays.asList(ints);

La méthode asList accepte un paramètre varargs qui, d'après mes connaissances, est un "raccourci" pour un tableau.

Des questions:

  • Pourquoi le deuxième morceau de code renvoie-t-il un List<int[]> mais non List<int>.

  • Y a-t-il un moyen de le corriger?

  • Pourquoi l'autoboxing ne fonctionne-t-il pas ici? c'est à dire. int[] à Integer[]?

49
Savvas Dalkitsis

Que dis-tu de ça?

Integer[] ints = new Integer[] {1,2,3,4,5};
List<Integer> list = Arrays.asList(ints);
26
JRL

Il n'y a pas de List<int> in Java - les génériques ne supportent pas les primitives.

L'autoboxing ne se produit que pour un seul élément, pas pour les tableaux de primitives.

Quant à la façon de le corriger, il existe différentes bibliothèques avec une multitude de méthodes pour faire des choses comme celle-ci. Il n'y a pas moyen de contourner cela, et je ne pense pas qu'il y ait quoi que ce soit pour faciliter les choses dans le JDK. Certains vont envelopper un tableau primitif dans une liste du type wrapper (de sorte que le boxing se produise lors de l'accès), d'autres vont parcourir le tableau d'origine pour créer un copie indépendante, boxe au fur et à mesure. Assurez-vous de savoir ce que vous utilisez.

(EDIT: je supposais que le point de départ d'un int[] n'était pas négociable. Si vous pouvez commencer par un Integer[] alors vous êtes bien loin :)

Juste pour un exemple de bibliothèque d'aide, et pour brancher un peu Guava , il y a com.google.common.primitive.Ints.asList .

69
Jon Skeet

Parce que Java les tableaux sont des objets et que Arrays.asList() traite votre tableau int comme un argument nique dans la liste varargs.

13
ChssPly76

Entrez Java 8, et vous pouvez faire ce qui suit pour collecter dans un tableau en boîte:

Integer[] boxedInts = IntStream.of(ints).boxed().toArray(Integer[]::new);

Ou ceci pour rassembler dans une liste encadrée

List<Integer> boxedInts = IntStream.of(ints).boxed().collect(Collectors.toList());

Cependant, cela ne fonctionne que pour int[], long[] Et double[]. Cela ne fonctionnera pas pour byte[].

Notez que Arrays.stream(ints) et IntStream.of(ints) sont équivalents. Ainsi, deux exemples précédents peuvent également être réécrits comme suit:

Integer[] boxedIntArray = Arrays.stream(ints).boxed().toArray(Integer[]::new);
List<Integer> boxedIntList = Arrays.stream(ints).boxed().collect(Collectors.toList());

Cette dernière forme pourrait être favorisée car elle omet un sous-type spécifique primitif de Stream. Cependant, en interne, c'est encore un tas de surchargés qui, dans ce cas, créent toujours un IntStream en interne.

12
YoYo

Le problème n'est pas avec Arrays.asList(). Le problème est que vous vous attendez à ce que l'autoboxing fonctionne sur un tableau - et ce n'est pas le cas. Dans le premier cas, le compilateur met automatiquement en boîte les entrées individuelles avant de regarder à quoi elles sont utilisées. Dans le second cas, vous les placez d'abord dans un tableau int (aucun autoboxing n'est nécessaire), puis vous le transmettez à Arrays.asList() (pas d'autoboxing possible).

4
Michael Borgwardt

Pourquoi l'autoboxing ne fonctionne-t-il pas ici? c'est-à-dire int [] à Integer []?

Alors que l'autoboxing convertira un int en un Integer, il ne convertira pas un int[] En un Integer[].

Pourquoi pas?

La réponse simple (mais insatisfaisante) est que c'est ce que dit le JLS. (Vous pouvez le vérifier si vous le souhaitez.)

La vraie réponse est fondamentale à ce que fait l'auto-boxe et pourquoi elle est sûre.

Lorsque vous effectuez une autobox 1 N'importe où dans votre code, vous obtenez le même objet Integer. Ce n'est pas le cas pour toutes les valeurs int (en raison de la taille limitée du cache de détection automatique Integer), mais si vous utilisez equals pour comparer les objets Integer vous obtenez la "bonne" réponse.

Fondamentalement, N == N Est toujours vrai et new Integer(N).equals(new Integer(N)) est toujours vrai. De plus, ces deux choses restent true ... en supposant que vous vous en tenez avec Pure Java code.

Considérez maintenant ceci:

int[] x = new int[]{1};
int[] y = new int[]{1};

Sont-ils égaux? Non! x == y Est faux et x.equals(y) est faux! Mais pourquoi? Car:

y[0] = 2;

En d'autres termes, deux tableaux avec le même type, la même taille et le même contenu sont toujours distinguables car Java sont mutables.

La "promesse" de la boxe automatique est qu'elle est acceptable car les résultats sont indiscernables1. Mais, parce que tous les tableaux sont fondamentalement distinguables en raison de la définition de equals pour les tableaux ET la mutabilité des tableaux. Donc, si l'autoboxing des tableaux de types primitifs était permis, cela saperait la "promesse".


1 - ..... à condition de ne pas utiliser == Pour tester si les valeurs autobox sont égales.

2
Stephen C

Arrays.asList(T... a) prend effectivement un T[] qui correspondra à n'importe quel tableau d'objets vrais (sous-classes de Object) comme tableau. La seule chose qui ne correspondra pas comme ça est un tableau de primitives, puisque les types primitifs ne dérivent pas de Object. Donc un int[] N'est pas un Object[].

Ce qui se passe alors, c'est que le mécanisme varags entre en action et le traite comme si vous aviez passé un seul objet, et crée un seul tableau d'éléments de ce type. Alors vous passez un int[][] (Ici, T est int[]) Et vous vous retrouvez avec un 1-élément List<int[]> Qui n'est pas ce que vous voulez.

Cependant, vous avez encore de très bonnes options:

Adaptateur Int.asList(int[]) de Guava

Si votre projet utilise déjà goyave, c'est aussi simple que d'utiliser l'adaptateur fourni par Guava: Int.asList () . Il existe un adaptateur similaire pour chaque type primitif de la classe associée, par exemple Booleans pour boolean, etc.

int foo[] = {1,2,3,4,5};
Iterable<Integer> fooBar = Ints.asList(foo);
for(Integer i : fooBar) {
    System.out.println(i);
}

L'avantage de cette approche est qu'elle crée un wrapper fin autour du tableau existant, de sorte que la création du wrapper est en temps constant (ne dépend pas de la taille du tableau), et le stockage requis n'est qu'une petite quantité constante ( moins de 100 octets) en plus du tableau d'entiers sous-jacent.

L'inconvénient est que l'accès à chaque élément nécessite une opération d'encadrement du int sous-jacent, et le paramétrage nécessite un déballage. Cela peut entraîner une grande quantité d'allocation de mémoire transitoire si vous accédez fortement à la liste. Si vous accédez à chaque objet plusieurs fois en moyenne, il peut être préférable d'utiliser une implémentation qui encadre les objets une fois et les stocke sous Integer. La solution ci-dessous fait cela.

Java 8 IntStream

Dans Java 8, vous pouvez utiliser la méthode Arrays.stream(int[]) pour transformer un tableau int en un Stream. Selon votre cas d'utilisation, vous pourrez peut-être utiliser le flux directement, par exemple pour faire quelque chose avec chaque élément avec forEach(IntConsumer) . Dans ce cas, cette solution est très rapide et n'entraîne aucune boxe ou unboxing du tout, et ne crée aucune copie du tableau sous-jacent.

Alternativement, si vous avez vraiment besoin d'un List<Integer>, Vous pouvez utiliser stream.boxed().collect(Collectors.toList()) comme suggéré ici . L'inconvénient de cette approche est qu'elle encadre complètement chaque élément de la liste, ce qui pourrait augmenter son empreinte mémoire de près d'un ordre de grandeur, elle crée un nouveau Object[] Pour contenir tous les éléments encadrés. Si par la suite vous utilisez beaucoup la liste et avez besoin d'objets Integer plutôt que ints, cela peut porter ses fruits, mais il faut en être conscient.

2
BeeOnRope

Si vous passez un int[] À Arrays.asList(), la liste créée sera List<int[]>, Ce qui n'est pas disponible en Java, pas le bon List<Integer>.

Je pense que vous vous attendez à ce que Arrays.asList() encapsule automatiquement vos entiers, ce qui, comme vous l'avez vu, ne le fera pas.

1
Tom Neyland

Il n'est pas possible de convertir int[] à Integer[], vous devez copier les valeurs


int[] tab = new int[]{1, 2, 3, 4, 5};
List<Integer> list = ArraysHelper.asList(tab);

public static List<Integer> asList(int[] a) {
    List<Integer> list = new ArrayList<Integer>();
    for (int i = 0; i < a.length && list.add(a[i]); i++);
    return list;
}
1
Maciek Kreft