web-dev-qa-db-fra.com

Convertir ArrayList <String> en tableau []

Je travaille dans l'environnement Android et j'ai essayé le code suivant, mais il ne semble pas fonctionner.

String [] stockArr = (String[]) stock_list.toArray();

Si je définis comme suit:

String [] stockArr = {"hello", "world"};

ça marche. Y a-t-il quelque chose qui me manque?

1077
locoboy

Utilisez comme ça.

List<String> stockList = new ArrayList<String>();
stockList.add("stock1");
stockList.add("stock2");

String[] stockArr = new String[stockList.size()];
stockArr = stockList.toArray(stockArr);

for(String s : stockArr)
    System.out.println(s);
1709
Prince John Wesley

Essaye ça

String[] arr = list.toArray(new String[list.size()]);
924
st0le

Qu'est-ce qui se passe est que stock_list.toArray() crée un Object[] plutôt qu'un String[] et donc le transtypage échoue1.

Le code correct serait:

  String [] stockArr = stockList.toArray(new String[stockList.size()]);

ou même

  String [] stockArr = stockList.toArray(new String[0]);

Pour plus de détails, reportez-vous aux javadocs pour les deux surcharges de List.toArray.

La dernière version utilise le tableau de longueur nulle pour déterminer le type du tableau de résultat. (Étonnamment, il est plus rapide de le faire que de préallouer ... du moins, pour les versions récentes de Java. Voir https://stackoverflow.com/a/4042464/139985 pour plus de détails.)

D'un point de vue technique, ce comportement/cette conception d'API s'explique par le fait qu'une implémentation de la méthode List<T>.toArray() ne contient aucune information sur ce que le <T> est à l'exécution. Tout ce qu'il sait, c'est que le type d'élément brut est Object. Dans le cas contraire, le paramètre array donne le type de base du tableau. (Si le tableau fourni est suffisamment grand pour contenir les éléments de la liste, il est utilisé. Sinon, un nouveau tableau du même type et de taille supérieure est alloué et renvoyé comme résultat.)


1 - En Java, un Object[] n'est pas une assignation compatible avec un String[]. Si c'était le cas, vous pourriez faire ceci:

    Object[] objects = new Object[]{new Cat("fluffy")};
    Dog[] dogs = (Dog[]) objects;
    Dog d = dogs[0];     // Huh???

C'est clairement absurde, et c'est pourquoi les types de tableaux ne sont généralement pas compatibles avec les affectations.

299
Stephen C

Une alternative dans Java 8:

String[] strings = list.stream().toArray(String[]::new);
147
Vitalii Fedorenko

Je peux voir beaucoup de réponses montrant comment résoudre le problème, mais seulement la réponse de Stephen essaie d'expliquer pourquoi le problème se produit, je vais donc essayer d'ajouter quelque chose de plus à ce sujet. C'est une histoire sur les raisons possibles pour lesquelles Object[] toArray n'a pas été remplacé par T[] toArray où les génériques ont été introduits en Java.


Pourquoi String[] stockArr = (String[]) stock_list.toArray(); ne fonctionnera pas?

En Java, le type générique existe uniquement à la compilation . Lors de l'exécution, les informations sur le type générique (comme dans votre cas <String>) sont supprimées et remplacées par le type Object (examinez type effacement ). C'est pourquoi au moment de l'exécution, toArray() n'a aucune idée du type précis à utiliser pour créer un nouveau tableau. Il utilise donc Object comme type le plus sûr, car chaque classe étend Object afin de pouvoir stocker en toute sécurité les instances d'une classe. .

Maintenant, le problème est que vous ne pouvez pas convertir une instance de Object[] en String[].

Pourquoi? Regardez cet exemple (supposons que class B extends A):

//B extends A
A a = new A();
B b = (B)a;

Bien que ce code soit compilé, au moment de l'exécution, nous verrons levé ClassCastException car l'instance conservée par la référence a n'est pas réellement de type B (ou de ses sous-types). Pourquoi ce problème est-il (pourquoi cette exception doit être lancée)? Une des raisons est que B pourrait avoir de nouvelles méthodes/champs que A n'est pas, il est donc possible que quelqu'un essaie d'utiliser ces nouveaux membres via b référence même s'il est tenu l'instance ne les a pas (ne les prend pas en charge). En d'autres termes, nous pourrions essayer d'utiliser des données qui n'existent pas, ce qui pourrait poser de nombreux problèmes. Donc, pour éviter une telle situation, JVM lève une exception et arrête le code potentiellement dangereux.

Vous pourriez demander maintenant "Alors pourquoi ne sommes-nous pas arrêtés encore plus tôt? Pourquoi un code impliquant un tel casting est même compilable? Le compilateur ne devrait-il pas l'arrêter?". La réponse est non, car le compilateur ne peut pas savoir avec certitude quel est le type d'instance détenue par la référence a, et il y a une chance qu'il contienne une instance de la classe B qui supportera l'interface de b référence. Jetez un oeil à cet exemple:

A a = new B(); 
      //  ^------ Here reference "a" holds instance of type B
B b = (B)a;    // so now casting is safe, now JVM is sure that `b` reference can 
               // safely access all members of B class

Revenons maintenant à vos tableaux. Comme vous le voyez en question, nous ne pouvons pas convertir l'instance de Object[] array en un type plus précis String[] comme

Object[] arr = new Object[] { "ab", "cd" };
String[] arr2 = (String[]) arr;//ClassCastException will be thrown

Ici le problème est un peu différent. Maintenant, nous sommes sûrs que le tableau String[] n'aura pas de champs ni de méthodes supplémentaires car chaque tableau ne prend en charge que:

  • [] opérateur,
  • length déposé,
  • méthodes héritées du supertype Object,

Donc, c’est pas l’interface des tableaux qui le rend impossible. Le problème est que Object[] Un tableau à côté de Strings peut stocker tous les objets ( par exemple Integers) il est donc possible qu'un beau jour nous finissions par essayer d'invoquer une méthode comme strArray[i].substring(1,3) sur une instance de Integer qui ne possède pas une telle méthode.

Donc, pour être sûr que cette situation ne se produira jamais , dans Java les références de tableau ne peuvent contenir que

  • instances de tableau du même type que la référence (la référence String[] strArr peut contenir String[])
  • les instances d'un tableau de sous-type (Object[] peuvent contenir String[] parce que String est un sous-type de Object),

mais ne peut pas tenir

  • tableau de supertype de type de tableau à partir de la référence (String[] ne peut contenir Object[])
  • tableau de type qui n'est pas lié au type de la référence (Integer[] ne peut contenir String[])

En d'autres termes, quelque chose comme ceci est OK

Object[] arr = new String[] { "ab", "cd" }; //OK - because
               //  ^^^^^^^^                  `arr` holds array of subtype of Object (String)
String[] arr2 = (String[]) arr; //OK - `arr2` reference will hold same array of same type as 
                                //     reference

Vous pouvez dire que l’un des moyens de résoudre ce problème consiste à rechercher au moment de l’exécution le type le plus courant entre tous les éléments de la liste et à créer un tableau de ce type, mais cela ne fonctionnera pas dans les situations où tous les éléments de la liste seront d’un type dérivé du type générique. Regarde

//B extends A
List<A> elements = new ArrayList<A>();
elements.add(new B());
elements.add(new B());

le type le plus courant est B et non A so toArray()

A[] arr = elements.toArray();

renverrait un tableau de B classe new B[]. Le problème avec ce tableau est que, bien que le compilateur vous permette d’éditer son contenu en y ajoutant l’élément new A(), vous obtiendrez ArrayStoreException car le tableau B[] ne peut contenir que des éléments de la classe B ou sa sous-classe, pour s’assurer que tous les éléments prendront en charge l’interface de B, mais l’instance de A peut ne pas avoir toutes les méthodes/tous les champs de B. Donc, cette solution n'est pas parfaite.


La meilleure solution à ce problème consiste à indiquer explicitement quel type de tableau toArray() devrait être renvoyé en passant ce type comme argument de méthode, comme

String[] arr = list.toArray(new String[list.size()]);

ou

String[] arr = list.toArray(new String[0]); //if size of array is smaller then list it will be automatically adjusted.
85
Pshemo

La bonne façon de faire est:

String[] stockArr = stock_list.toArray(new String[stock_list.size()]);

J'aimerais ajouter aux autres bonnes réponses ici et expliquer comment vous auriez pu utiliser les Javadocs pour répondre à votre question.

La Javadoc pour toArray() (sans argument) est ici . Comme vous pouvez le constater, cette méthode retourne un Object[] et pas String[] qui est un tableau du type d'exécution de votre liste:

public Object[] toArray()

Retourne un tableau contenant tous les éléments de cette collection. Si la collection donne des garanties quant à l'ordre dans lequel ses éléments sont renvoyés par son itérateur, cette méthode doit renvoyer les éléments dans le même ordre. Le tableau retourné sera "sûr" en ce sens que la collection ne conservera aucune référence. (En d'autres termes, cette méthode doit allouer un nouveau tableau même si la collection est sauvegardée par un tableau). L'appelant est donc libre de modifier le tableau retourné.

Juste en dessous de cette méthode, cependant, se trouve the Javadoc pour toArray(T[] a). Comme vous pouvez le constater, cette méthode retourne un T[]T est le type du tableau que vous transmettez. Au début, cela semble être ce que vous recherchez, mais on ne sait pas exactement pourquoi vous êtes. passer dans un tableau (ajoutez-vous à cela, utilisez-le uniquement pour le type, etc.). La documentation indique clairement que l'objectif du tableau transmis est essentiellement de définir le type de tableau à renvoyer (ce qui correspond exactement à votre cas d'utilisation):

public <T> T[] toArray(T[] a)

Retourne un tableau contenant tous les éléments de cette collection. le type d'exécution du tableau renvoyé est celui du tableau spécifié. Si la collection s'inscrit dans le tableau spécifié, elle y est retournée. Sinon, un nouveau tableau est alloué avec le type d'exécution du tableau spécifié et la taille de cette collection. Si la collection s'inscrit dans le tableau spécifié avec un espace disponible (c'est-à-dire que le tableau contient plus d'éléments que la collection), l'élément du tableau qui suit immédiatement la fin de la collection est défini sur null. Ceci est utile pour déterminer la longueur de la collection uniquement si l'appelant sait que la collection ne contient aucun élément null.)

Si cette collection donne des garanties quant à l'ordre dans lequel ses éléments sont renvoyés par son itérateur, cette méthode doit renvoyer les éléments dans le même ordre.

Cette implémentation vérifie si le tableau est assez grand pour contenir la collection. sinon, il alloue un nouveau tableau de taille et de type corrects (en utilisant la réflexion). Ensuite, il parcourt la collection en stockant chaque référence d'objet dans le prochain élément consécutif du tableau, en commençant par l'élément 0. Si le tableau est plus grand que la collection, un null est stocké au premier emplacement après la fin de la collection.

Bien entendu, une compréhension des génériques (comme décrit dans les autres réponses) est nécessaire pour bien comprendre la différence entre ces deux méthodes. Néanmoins, si vous vous adressez d'abord aux Javadocs, vous trouverez généralement votre réponse, puis vous pourrez voir par vous-même ce que vous devez encore apprendre (le cas échéant).

Notez également que la lecture des Javadocs ici vous aide à comprendre quelle doit être la structure du tableau que vous transmettez. Bien que cela n’ait pas vraiment d'importance dans la pratique, vous ne devriez pas passer dans un tableau vide comme ceci:

String [] stockArr = stockList.toArray(new String[0]);  

Parce que, depuis le doc, cette implémentation vérifie si le tableau est assez grand pour contenir la collection; sinon, il alloue un nouveau tableau de la taille et du type appropriés (en utilisant la réflexion). Il n'est pas nécessaire de prendre plus de temps pour créer un nouveau tableau alors que vous pouviez facilement passer le Taille.

Comme d'habitude, les Javadocs vous fournissent une mine d'informations et de conseils.

Hey attends une minute, quelle réflexion?

20
Rick Hanlon II