it-swarm.com.de

Konvertieren Sie ArrayList <String> in das Array String []

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

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

Wenn ich wie folgt definiere:

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

es klappt. Fehlt mir etwas?

1077
locoboy

Verwenden Sie wie folgt.

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

Versuche dies

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

Was passiert, ist, dass stock_list.toArray() einen Object[] erstellt und nicht einen 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]);

Weitere Informationen zu den beiden Überladungen von List.toArray finden Sie in den Javadocs.

Die letztere Version verwendet das Array mit der Länge Null, um den Typ des Ergebnis-Arrays zu bestimmen. (Es ist überraschenderweise schneller, dies zu tun, als ... zumindest für neuere Java Releases vorab zuzuweisen. Siehe https://stackoverflow.com/a/4042464/139985 für Details.)

Aus technischer Sicht liegt der Grund für dieses API-Verhalten/-Design darin, dass eine Implementierung der List<T>.toArray() -Methode keine Informationen darüber enthält, was der <T> zur Laufzeit ist. Es ist lediglich bekannt, dass der Rohelementtyp Object ist. Im Gegensatz dazu gibt der Array-Parameter den Basistyp des Arrays an. (Wenn das bereitgestellte Array groß genug ist, um die Listenelemente aufzunehmen, wird es verwendet. Andernfalls wird ein neues Array des gleichen Typs und einer größeren Größe zugewiesen und als Ergebnis zurückgegeben.)


1 - In Java ist ein Object[] nicht zu einem String[] zuweisungskompatibel. Wenn ja, könnten Sie dies 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 im Allgemeinen nicht zuweisungskompatibel.

299
Stephen C

Eine Alternative in Java 8:

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

Ich kann viele Antworten sehen, die zeigen, wie das Problem gelöst werden kann, aber nur Stephens Antwort versucht zu erklären, warum das 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 nicht in T[] toArray geändert wurde, wo Generika in Java eingeführt wurden.


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

In Java ist der generische Typ nur zur Kompilierungszeit vorhanden . Zur Laufzeit werden Informationen zum generischen Typ (wie in Ihrem Fall <String>) entfernt und durch Object type ersetzt (siehe type erasure ). Das ist der Grund, warum toArray() zur Laufzeit keine Ahnung hat, welchen genauen Typ Sie zum Erstellen eines neuen Arrays verwenden müssen. Daher wird Object als sicherster Typ verwendet, da jede Klasse Object erweitert, um die Instanz einer Klasse sicher zu speichern.

Das Problem ist nun, dass Sie die Instanz von Object[] nicht in String[] umwandeln können.

Warum? Schauen 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 ein solcher Code kompiliert wird, wird zur Laufzeit ClassCastException ausgegeben, da die durch die Referenz a gespeicherte Instanz nicht vom Typ B (oder dessen Untertypen) ist. Warum ist dieses Problem (warum muss diese Ausnahme besetzt werden)? Einer der Gründe ist, dass B neue Methoden/Felder haben könnte, die A nicht hat. Daher ist es möglich, dass jemand versucht, diese neuen Mitglieder über die b Referenz zu verwenden, auch wenn die gehaltene Instanz sie nicht hat (nicht unterstützt) . Mit anderen Worten, wir könnten versuchen, Daten zu verwenden, die nicht existieren, was zu vielen Problemen führen könnte. Um eine solche Situation zu verhindern, löst JVM eine Ausnahme aus und stoppt weiteren potenziell gefährlichen Code.

Sie könnten jetzt fragen: "Warum werden wir nicht noch früher gestoppt? Warum ist Code, der ein solches Casting beinhaltet, überhaupt kompilierbar? Sollte der Compiler es nicht stoppen?". Die Antwort lautet: nein, da der Compiler nicht genau wissen kann, welcher Instanztyp von a referenziert wird, und es besteht die Möglichkeit, dass er eine Instanz der Klasse B enthält, die die Schnittstelle von b referenz unterstützt. Schauen Sie sich 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

Kehren wir nun zu Ihren Arrays zurück. Wie Sie in Frage sehen, können wir die Instanz des Arrays Object[] nicht auf den genaueren Typ String[] umwandeln

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

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

  • [] Operator,
  • length abgelegt,
  • von Object supertype geerbte Methoden,

Es ist also keine Array-Schnittstelle, die dies unmöglich macht. Das Problem ist, dass das Array Object[] neben Strings beliebige Objekte speichern kann (zum Beispiel Integers) Es ist also möglich, dass wir eines schönen Tages versuchen werden, eine Methode wie strArray[i].substring(1,3) für eine Instanz von Integer aufzurufen, die keine solche Methode hat.

Um sicherzustellen , dass diese Situation niemals eintreten wird, können in Java Array-Referenzen nur enthalten sein

  • instanzen eines Arrays des gleichen Typs wie Referenz (Referenz String[] strArr kann String[] enthalten)
  • instanzen eines Arrays des Subtyps (Object[] kann String[] enthalten, da String der Subtyp von Object ist),

kann aber nicht halten

  • array des Supertyps des Array-Typs aus der Referenz (String[] kann Object[] nicht enthalten)
  • array des Typs, der nicht mit dem Typ aus der Referenz zusammenhängt (Integer[] kann String[] nicht enthalten)

Mit anderen Worten ist so etwas 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

Eine Möglichkeit, dieses Problem zu beheben, besteht darin, zur Laufzeit den gebräuchlichsten Typ zwischen allen Listenelementen zu finden und ein Array dieses Typs zu erstellen. Dies funktioniert jedoch nicht in Situationen, in denen alle Elemente einer Liste von einem generischen Typ abgeleitet sind. Schau mal

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

jetzt ist der gebräuchlichste Typ B, nicht A, also toArray()

A[] arr = elements.toArray();

würde ein Array von B class new B[] zurückgeben. Das Problem mit diesem Array ist, dass der Compiler es Ihnen ermöglicht, den Inhalt zu bearbeiten, indem Sie das Element new A() hinzufügen. Sie erhalten jedoch ArrayStoreException, da das Array B[] nur Elemente der Klasse B oder deren Unterklasse enthalten kann Stellen Sie sicher, dass alle Elemente die Schnittstelle von B unterstützen, aber die Instanz von A enthält möglicherweise nicht alle Methoden/Felder von B. Diese Lösung ist also nicht perfekt.


Die beste Lösung für dieses Problem besteht darin, explizit anzugeben, welcher Typ von Array toArray() zurückgegeben werden soll, 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.
85
Pshemo

Der richtige Weg dies zu tun ist:

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

Ich möchte die anderen tollen Antworten hier ergänzen und erklären, wie Sie die Javadocs zur Beantwortung Ihrer Frage hätten verwenden können.

Das Javadoc für toArray() (keine Argumente) ist hier . Wie Sie sehen können, gibt diese Methode einen Object[] und not String[] zurück. Dies ist ein Array des Laufzeittyps Ihrer Liste:

public Object[] toArray()

Gibt ein Array zurück, das alle Elemente in dieser Auflistung enthält. Wenn die Auflistung garantiert, in welcher Reihenfolge ihre Elemente vom Iterator zurückgegeben werden, muss diese Methode die Elemente in derselben Reihenfolge zurückgeben. Das zurückgegebene Array ist "sicher", da von der Auflistung keine Verweise darauf verwaltet werden. (Mit anderen Worten, diese Methode muss ein neues Array zuweisen, auch wenn die Auflistung von einem Array unterstützt wird.) Dem Aufrufer steht es somit frei, das zurückgegebene Array zu ändern.

Gleich unterhalb dieser Methode befindet sich jedoch das Javadoc für toArray(T[] a). Wie Sie sehen können, gibt diese Methode einen T[] zurück, wobei T der Typ des Arrays ist, in das Sie übergeben. Zuerst scheint dies das zu sein, wonach Sie suchen, aber es ist nicht klar, warum Sie genau suchen Übergeben eines Arrays (fügen Sie es hinzu und verwenden es nur für den Typ usw.). 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 Elemente in dieser Auflistung enthält. Der Laufzeit-Typ des zurückgegebenen Arrays ist der des angegebenen Arrays. Wenn die Auflistung in das angegebene Array passt, wird sie darin zurückgegeben. Andernfalls wird ein neues Array mit dem Laufzeittyp des angegebenen Arrays und der Größe dieser Auflistung zugewiesen. Wenn die Auflistung in das angegebene Array mit freiem Speicherplatz passt (d. H. Das Array enthält mehr Elemente als die Auflistung), wird das Element in dem Array, das unmittelbar auf das Ende der Auflistung folgt, auf null gesetzt. Dies ist nur dann hilfreich, wenn der Aufrufer weiß, dass die Auflistung keine Null-Elemente enthält, um die Länge der Auflistung zu bestimmen.)

Wenn diese Auflistung garantiert, in welcher Reihenfolge die Elemente von ihrem Iterator zurückgegeben werden, muss diese Methode die Elemente in derselben Reihenfolge zurückgeben.

Diese Implementierung überprüft, ob das Array groß genug ist, um die Auflistung zu enthalten. Andernfalls wird ein neues Array mit der richtigen Größe und dem richtigen Typ zugewiesen (mithilfe von Reflektion). Anschließend durchläuft es die Auflistung und speichert jede Objektreferenz im nächsten aufeinanderfolgenden Element des Arrays, beginnend mit Element 0. Wenn das Array größer als die Auflistung ist, wird an der ersten Stelle nach dem Ende der Auflistung eine Null gespeichert.

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

Beachten Sie auch, dass Sie durch Lesen der Javadocs hier die Struktur des Arrays, das Sie übergeben, besser verstehen können. Auch wenn es praktisch keine Rolle spielt, sollten Sie kein leeres Array wie das folgende übergeben:

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

Da diese Implementierung anhand des Dokuments prüft, ob das Array groß genug ist, um die Auflistung zu enthalten. Andernfalls wird ein neues Array mit der richtigen Größe und dem richtigen Typ zugewiesen (mithilfe von Reflektion). Das Erstellen eines neuen Arrays erfordert keinen zusätzlichen Aufwand, wenn Sie das Array problemlos übergeben könnten Größe.

Wie immer bieten Ihnen die Javadocs eine Fülle von Informationen und Anleitungen.

Hey, warte mal, was ist das für ein Spiegelbild?

20
Rick Hanlon II