Frage Konvertieren ArrayList in String [] Array [Duplizieren]


Diese Frage hat hier bereits eine Antwort:

Ich arbeite in der Android-Umgebung und habe den folgenden Code ausprobiert, aber es scheint nicht zu funktionieren.

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

Wenn ich folgendes definiere:

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

Es klappt. Gibt es etwas, das ich vermisse?


903
2018-03-21 05:57


Ursprung


Antworten:


Verwenden Sie so.

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



Versuche dies

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

818
2018-03-21 06:03



Was ist passiert? stock_list.toArray() schafft ein Object[] eher als ein String[] und daher schlägt die Typumwandlung fehl1.

Der richtige Code wäre:

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

oder auch

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

(Überraschenderweise ist die letztere Version in den letzten Java-Versionen schneller: siehe https://stackoverflow.com/a/4042464/139985)

Weitere Einzelheiten finden Sie in den Javadocs für die beiden Überladungen von List.toArray.

(Aus technischer Sicht ist der Grund für dieses API - Verhalten / Design, dass eine Implementierung des List<T>.toArray() Methode hat keine Informationen darüber, was <T> ist zur Laufzeit. Alles, was es weiß, ist, dass der rohe Elementtyp ist Object. Im Gegensatz dazu gibt der Array-Parameter im anderen Fall den Basistyp des Arrays an. (Wenn das angegebene Array groß genug ist, wird es verwendet. Andernfalls wird ein neues Array desselben Typs und einer größeren Größe zugewiesen und als Ergebnis zurückgegeben.)


1 - In Java, ein Object[] ist nicht mit einer Zuordnung kompatibel String[]. Wenn es war, dann könntest du das tun:

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

Dies ist eindeutig Unsinn, und deshalb sind Array-Typen in der Regel nicht zuweisungskompatibel.


254
2018-03-21 06:05



Eine Alternative in Java 8:

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

114
2018-04-20 01:58



Ich kann viele Antworten sehen, die zeigen, wie man das Problem löst, aber nur Stephens Antwort versucht zu erklären, warum ein Problem auftritt, also werde ich versuchen, etwas mehr zu diesem Thema hinzuzufügen. Es ist eine Geschichte über mögliche Gründe warum Object[] toArray wurde nicht geändert T[] toArray wo Generika in Java eingeführt wurden.


Warum String[] stockArr = (String[]) stock_list.toArray(); wird nicht arbeiten?

In Java, Der generische Typ existiert nur zur Kompilierzeit. Zur Laufzeit Informationen zum generischen Typ (wie in Ihrem Fall <String>) wird entfernt und durch ersetzt Object tippen Sie ein (werfen Sie einen Blick auf Typ löschen). Deshalb zur Laufzeit toArray() Ich habe keine Ahnung, welcher genaue Typ verwendet werden soll, um ein neues Array zu erstellen Object als sicherster Typ, da jede Klasse Object erweitert, so dass sie die Instanz jeder Klasse sicher speichern kann.

Jetzt ist das Problem, dass Sie Instanz von nicht werfen können Object[] zu String[].

Warum? Sehen Sie sich dieses Beispiel an (nehmen wir an, dass class B extends A):

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

Obwohl dieser Code kompiliert wird, werden wir zur Laufzeit geworfen sehen ClassCastException weil die Instanz durch Verweis gehalten wird a ist eigentlich nicht vom Typ B (oder seine Subtypen). Warum ist dieses Problem (warum muss diese Ausnahme umgesetzt werden)? Einer der Gründe ist, dass B könnte neue Methoden / Felder haben, die A nicht, also ist es möglich, dass jemand versucht, diese neuen Mitglieder über zu verwenden b Referenz, auch wenn die gehaltene Instanz sie nicht unterstützt (nicht unterstützt). Mit anderen Worten könnten wir am Ende versuchen, Daten zu verwenden, die nicht existieren, was zu vielen Problemen führen könnte. Um solche Situationen zu vermeiden, gibt JVM eine Ausnahme aus und stoppt weiteren potenziell gefährlichen Code.

Du könntest jetzt fragen: "Warum werden wir nicht schon früher gestoppt? Warum ist Code, der ein solches Casting beinhaltet, sogar kompilierbar? Sollte der Compiler das nicht stoppen?". Die Antwort lautet: Nein, weil der Compiler nicht genau wissen kann, um was für einen Instanztyp es sich handelt a Referenz, und es gibt eine Chance, dass es Instanz der Klasse halten wird B welche Schnittstelle von unterstützen wird b Referenz. Schau dir dieses Beispiel an:

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

Jetzt gehen wir zurück zu Ihren Arrays. Wie Sie sehen, können wir keine Instanz von Object[] Array zu genauerem Typ String[] mögen

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

Hier ist das Problem ein wenig anders. Jetzt sind wir sicher, dass String[] Array hat keine zusätzlichen Felder oder Methoden, da jedes Array nur unterstützt:

  • [] Operator,
  • length eingereicht,
  • Methoden, die von Object supertype geerbt wurden,

So ist es nicht Array-Schnittstelle, die es unmöglich macht. Problem ist das Object[] Array neben Strings kann beliebige Objekte speichern (zum Beispiel Integers) so ist es möglich, dass wir an einem schönen Tag am Ende versuchen werden, Methoden aufzurufen wie strArray[i].substring(1,3) zum Beispiel von Integer das hat keine solche Methode.

Also machen sicher dass diese Situation wird noch nie In Java können Array-Referenzen nur vorkommen

  • Instanzen von Array des gleichen Typs wie Referenz (Referenz String[] strArr kann halten String[])
  • Instanzen des Arrays des Subtyps (Object[] kann halten String[] weil String ist Untertyp von Object),

aber kann nicht halten

  • Array von Obertyp des Array-Typs aus Referenz (String[] kann nicht halten Object[])
  • Array des Typs, der nicht vom Typ aus Referenz (Integer[] kann nicht halten String[])

Mit anderen Worten, so etwas ist in Ordnung

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

Man könnte sagen, dass eine Möglichkeit, dieses Problem zu lösen, darin besteht, zur Laufzeit den am häufigsten vorkommenden Typ zwischen allen Listenelementen zu finden und ein Array dieses Typs zu erstellen, aber dies funktioniert nicht in Situationen, in denen alle Elemente der Liste von einem generischen Typ sind. Schau mal

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

jetzt am häufigsten ist Typ Bnicht A damit toArray() 

A[] arr = elements.toArray();

würde Array von zurückgeben B Klasse new B[]. Das Problem mit diesem Array besteht darin, dass der Compiler es Ihnen erlauben würde, seinen Inhalt durch Hinzufügen zu bearbeiten new A() Element dazu würden Sie bekommen ArrayStoreException weil B[] Array kann nur Elemente der Klasse enthalten B oder seine Unterklasse, um sicherzustellen, dass alle Elemente die Schnittstelle von unterstützen B, aber Instanz von A möglicherweise nicht alle Methoden / Felder von B. Diese Lösung ist also nicht perfekt.


Die beste Lösung für dieses Problem ist explizit sagen, welche Art von Array toArray() sollte zurückgegeben werden, indem dieser Typ als Methodenargument übergeben wird

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

oder

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



Der richtige Weg, dies zu tun ist:

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

Ich möchte zu den anderen tollen Antworten hier hinzufügen und erklären, wie Sie die Javadocs verwendet haben könnten, um Ihre Frage zu beantworten.

Der Javadoc für toArray() (keine Argumente) ist Hier. Wie Sie sehen können, gibt diese Methode ein Object[] und nicht  String[] Dies ist ein Array des Laufzeittyps Ihrer Liste:

public Object[] toArray() 

Gibt ein Array zurück, das alle enthält   Elemente in dieser Sammlung. Wenn die Sammlung irgendwelche Garantien gibt wie   In welcher Reihenfolge werden seine Elemente vom Iterator zurückgegeben, diese Methode   muss die Elemente in der gleichen Reihenfolge zurückgeben. Das zurückgegebene Array wird sein   "sicher", da keine Verweise darauf von der Sammlung gepflegt werden.   (Mit anderen Worten, diese Methode muss ein neues Array zuweisen, auch wenn der   Sammlung wird durch ein Array unterstützt). Der Anrufer kann somit frei ändern   das zurückgegebene Array.

Genau unter dieser Methode ist jedoch der Javadoc zum toArray(T[] a). Wie Sie sehen können, gibt diese Methode a zurück T[] woher T ist der Typ des Arrays, an dem Sie teilnehmen. Zuerst scheint das, was Sie suchen, aber es ist nicht klar, warum Sie ein Array übergeben (fügen Sie hinzu, verwenden Sie es nur für den Typ, etc) . Die Dokumentation macht deutlich, dass der Zweck des übergebenen Arrays im Wesentlichen darin besteht, den Typ des zurückzugebenden Arrays zu definieren (was genau Ihr Anwendungsfall ist):

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

Gibt ein Array zurück, das alle enthält   Elemente in dieser Sammlung; Der Laufzeittyp des zurückgegebenen Arrays ist   das des angegebenen Arrays. Wenn die Sammlung in das angegebene passt   Array, es wird darin zurückgegeben. Andernfalls wird ein neues Array zugewiesen   mit dem Laufzeittyp des angegebenen Arrays und dessen Größe   Sammlung. Wenn die Sammlung in das angegebene Array mit Platz passt   spare (d. h. das Array hat mehr Elemente als die Sammlung), die   Element im Array unmittelbar nach dem Ende der Sammlung   ist auf null gesetzt. Dies ist nützlich bei der Bestimmung der Länge der   Sammlung nur, wenn der Anrufer weiß, dass die Sammlung nicht   enthält alle Nullelemente.)

Wenn diese Sammlung irgendwelche Garantien gibt, in welcher Reihenfolge ihre Elemente   Wenn die Methode von ihrem Iterator zurückgegeben wird, muss diese Methode die Elemente zurückgeben   die gleiche Reihenfolge.

Diese Implementierung überprüft, ob das Array groß genug ist, um das Array zu enthalten   Sammlung; Wenn nicht, weist es ein neues Array der richtigen Größe zu   Typ (mit Reflektion). Dann iteriert es über die Sammlung,   Speichern jedes Objektverweises im nächsten fortlaufenden Element des   Array, beginnend mit Element 0. Wenn das Array größer ist als das   collection wird nach dem Ende von null an der ersten Stelle gespeichert   die Sammlung.

Natürlich ist ein Verständnis von Generika (wie in den anderen Antworten beschrieben) erforderlich, um den Unterschied zwischen diesen beiden Methoden wirklich zu verstehen. Dennoch, wenn Sie zuerst zu den Javadocs gehen, werden Sie normalerweise Ihre Antwort finden und dann selbst sehen, was Sie sonst noch lernen müssen (wenn Sie wirklich tun).

Beachten Sie auch, dass das Lesen der Javadocs hier hilft zu verstehen, wie die Struktur des Arrays aussehen soll, die Sie übergeben. Auch wenn es praktisch nicht wichtig ist, sollten Sie ein leeres Array nicht so übergeben:

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

Diese Implementierung prüft anhand des Dokuments, ob das Array groß genug ist, um die Sammlung zu enthalten. Ist dies nicht der Fall, wird ein neues Array der richtigen Größe und des richtigen Typs zugewiesen (mithilfe der Reflektion). Der zusätzliche Aufwand beim Erstellen eines neuen Arrays entfällt, wenn Sie einfach die Größe übergeben können.

Wie es normalerweise der Fall ist, bieten Ihnen die Javadocs eine Fülle von Informationen und Anweisungen.

Hey, warte mal, was ist Reflektion?


16
2017-07-28 15:31