Question Convertir ArrayList en String [] array [dupliquer]


Cette question a déjà une réponse ici:

Je travaille dans l'environnement android et j'ai essayé le code suivant, mais ça 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?


903
2018-03-21 05:57


origine


Réponses:


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

1504
2018-03-21 06:07



Essaye ça

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

818
2018-03-21 06:03



Qu'est-ce qui se passe est que stock_list.toArray() crée un Object[] Plutôt qu'un String[] et par conséquent le typecast est défaillant1.

Le code correct serait:

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

ou même

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

(Étonnamment, cette dernière version est plus rapide dans les versions récentes de Java: voir https://stackoverflow.com/a/4042464/139985)

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

(D'un point de vue technique, la raison de ce comportement / conception de l'API est qu'une implémentation de List<T>.toArray() méthode n'a aucune information de ce que le <T> est à l'exécution. Tout ce qu'il sait, c'est que le type d'élément brut est Object. En revanche, dans l'autre cas, le paramètre array donne le type de base du tableau. (Si le tableau fourni est assez grand, il est utilisé, sinon un nouveau tableau du même type et d'une plus grande taille sera alloué et retourné comme résultat.)


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

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

Ceci est clairement un non-sens, et c'est pourquoi les types de tableaux ne sont généralement pas compatibles.


254
2018-03-21 06:05



Une alternative en Java 8:

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

114
2018-04-20 01:58



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 donc je vais essayer d'ajouter quelque chose de plus sur ce sujet. C'est une histoire sur les raisons possibles Object[] toArray n'a pas été changé en T[] toArray où les produits génériques introduits à Java.


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

En Java, type générique existe à la compilation seulement. À l'exécution des informations sur le type générique (comme dans votre cas <String>) est enlevé et remplacé par Object tapez (jetez un oeil à effacement de type). C'est pourquoi à l'exécution toArray() n'ai aucune idée de quel type précis utiliser pour créer un nouveau tableau, de sorte qu'il utilise Object comme le type le plus sûr, car chaque classe étend Object afin qu'il puisse stocker en toute sécurité l'instance de n'importe quelle classe.

Maintenant, le problème est que vous ne pouvez pas lancer l'instance de Object[] à String[].

Pourquoi? Jetez un oeil à cet exemple (supposons que class B extends A):

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

Bien que ce code compilera, à l'exécution, nous verrons jeté ClassCastException parce que l'instance détenue par référence a n'est pas réellement de type B (ou ses sous-types). Pourquoi est ce problème (pourquoi cette exception doit-elle être lancée)? Une des raisons est que B pourrait avoir de nouvelles méthodes / champs A ne le fait pas, il est donc possible que quelqu'un essaie d'utiliser ces nouveaux membres via b référence même si l'instance tenue n'a pas (ne supporte pas) eux. En d'autres termes, nous pourrions finir par essayer d'utiliser des données qui n'existent pas, ce qui pourrait entraîner de nombreux problèmes. Donc, pour éviter une telle situation, la JVM lance une exception et arrête d'autres codes potentiellement dangereux.

Vous pouvez demander maintenant "Alors pourquoi ne sommes nous pas arrêtés encore plus tôt? Pourquoi le code impliquant un tel casting est même compilable? Le compilateur ne devrait-il pas l'arrêter?". La réponse est: non parce que le compilateur ne peut pas savoir avec certitude quel est le type réel d'instance détenue par a référence, et il y a une chance qu'il tiendra l'instance de la classe B qui prendra en charge 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

Maintenant, revenons à vos tableaux. Comme vous le voyez en question, nous ne pouvons pas lancer d'instance de Object[] tableau à 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 String[] Le tableau n'aura pas de champs ou de méthodes supplémentaires car chaque tableau ne supporte que:

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

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

Donc faire sûr que cette situation jamais arriver, dans les références de tableau Java ne peut contenir que

  • instances de tableau du même type que la référence (référence String[] strArr peut tenir String[])
  • instances de tableau de sous-type (Object[] peut tenir String[] car String est sous-type de Object),

mais ne peut pas tenir

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

En d'autres termes, quelque chose comme ça 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 pourriez dire qu'une façon de résoudre ce problème est de trouver au moment de l'exécution le type le plus commun entre tous les éléments de la liste et de créer un tableau de ce type, mais cela ne fonctionnera pas dans tous les cas. Regarde

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

maintenant le type le plus commun est B, ne pas A alors toArray() 

A[] arr = elements.toArray();

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


La meilleure solution à ce problème est de dire explicitement quel type de tableau toArray() devrait être retourné en passant ce type comme argument de la 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.

81
2017-07-28 14:19



La bonne façon de le faire est la suivante:

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

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

Le Javadoc pour toArray() (pas d'arguments) est ici. Comme vous pouvez le voir, cette méthode retourne un Object[] et ne 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   dans quel ordre 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 qu'aucune référence n'est conservée par la collection.   (En d'autres termes, cette méthode doit allouer un nouveau tableau même si   la collection est soutenue par un tableau). L'appelant est donc libre de modifier   le tableau retourné.

Juste en dessous de cette méthode, cependant, est le Javadoc pour toArray(T[] a). Comme vous pouvez le voir, cette méthode retourne un T[] où T est le type du tableau que vous transmettez. Au début, cela ressemble à ce que vous cherchez, mais on ne sait pas exactement pourquoi vous passez dans un tableau (ajoutez-vous, n'utilisez que le type, etc.) . La documentation indique clairement que le but du tableau passé est essentiellement de définir le type de tableau à retourner (ce qui est 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   celle du tableau spécifié. Si la collection correspond à la spécification   tableau, il est retourné dedans. Sinon, un nouveau tableau est alloué   avec le type d'exécution du tableau spécifié et la taille de ce   collection. Si la collection s'inscrit dans le tableau spécifié avec de la place pour   spare (c'est-à-dire que le tableau contient plus d'éléments que la collection),   élément dans le tableau immédiatement après la fin de la collection   est défini sur null. Ceci est utile pour déterminer la longueur de   collection uniquement si l'appelant sait que la collection ne   contenir des éléments null.)

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

Cette implémentation vérifie si le tableau est assez grand pour contenir le   collection; sinon, il alloue un nouveau tableau de la bonne taille et   type (en utilisant la réflexion). Ensuite, il itère sur la collection,   stocker chaque référence d'objet dans l'élément consécutif suivant de la   array, en commençant par l'élément 0. Si le tableau est plus grand que   collection, une valeur nulle est stockée dans le premier emplacement après la fin de   la collection.

Bien sûr, une compréhension des génériques (comme décrit dans les autres réponses) est nécessaire pour vraiment comprendre la différence entre ces deux méthodes. Néanmoins, si vous allez d'abord aux Javadocs, vous trouverez généralement votre réponse, puis vous verrez par vous-même ce que vous devez apprendre (si vous le faites vraiment).

Notez également que la lecture des Javadocs ici vous aide à comprendre quelle devrait être la structure du tableau que vous transmettez. Bien que cela ne soit pas vraiment important, vous ne devriez pas passer dans un tableau vide comme ceci:

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

Parce que, à partir de la 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 corrects (en utilisant la réflexion). Il n'y a pas besoin de frais supplémentaires dans la création d'un nouveau tableau lorsque vous pouvez facilement passer dans la taille.

Comme c'est généralement le cas, les Javadocs vous fournissent une mine d'informations et de conseils.

Hey attendez une minute, quelle est la réflexion?


16
2017-07-28 15:31