2016-06-24 9 views
7

Ich arbeitete am Collection-Framework in Java, wo ich auf ein seltsames Problem stieß. Ich habe 2 Listen von Strings 1 mit Hilfe von ArrayList gemacht, während zweite mit Arrays.asList (T ...) gemacht wurde.Unterschiedliches Verhalten beim Konvertieren einer String-Liste in ein String-Array

Nach der Erstellung dieser beiden Liste i versucht, diese Listen in String-Arrays mit dem list.toArray() zu konvertieren,
als list.toArray() Methodenaufruf ein Objektarray zurückgibt, so musste ich explizit String gieße [].

Nachdem ein seltsames Verhalten Gießen geschieht wie:

Fall # 1: (wobei die Liste erstellt wurde mit Arraylist), Laufzeitausnahme als java.lang.ClassCastException gibt: [Ljava.lang.Object; kann nicht in [Ljava.lang.String;

Fall 2: (wo Liste wie erstellt mit Arrays.asList (T ...)) läuft gut.

hier ist der Code

String [] str = null ,str1 = null ; 
List<String> list = new ArrayList<String>(); 
list.add("a"); 
List<String> list1 = Arrays.asList("a"); 
str = (String[]) list.toArray(); // Runtime Exception 
str1 = (String[]) list1.toArray(); // Runs Fine 

Antwort

7

Ein ArrayList durch einen Object[] unterstützt wird. Eine Kopie dieses Arrays wird mit toArray() zurückgegeben.

Gibt ein Array zurück, das alle Elemente in dieser Liste in der richtigen Sequenz (vom ersten bis zum letzten Element) enthält.

Es gibt keine Garantien für den Typ des zurückgegebenen Arrays. Aber das wissen wir aus der Nachricht der Ausnahme. Wenn Sie möchten, dass eine String[] zurückgegeben wird, verwenden Sie die überladene Methode, die aus diesem Grund bereitgestellt wird.

str = list.toArray(new String[0]); 

Die Besetzung wird unnötig.

Die von Arrays.asList zurückgegebene List Implementierung enthält einen Verweis auf das Array (implizit oder explizit), das als variables Arity-Argument übergeben wird.

Gibt eine Liste fester Größe zurück, die vom angegebenen Array unterstützt wird.

Der Aufruf

List<String> list1 = Arrays.asList("a"); 

schafft eine String[] und übergibt, der als Argument asList. Dies wird nicht eindeutig durch die API-Dokumentation angegeben, aber unterstützt durch scheint anzuzeigen, dass es das gleiche Array zurückgeben wird. (Betrachtet man die Implementierung, wird ein Klon zurückgegeben.) Da es sich um eine String[] handelt, gibt es keinen Fehler beim Umwandeln und Zuweisen einer Variablen dieses Typs.


In beiden Fällen ist die geeignete Lösung, um die überlasteten List#toArray(T[]) zu verwenden.


Zum Spaß, führen Sie die folgenden und überprüfen Sie den Typ des Arrays, die zurückgegeben wird.

List<String> list1 = (List) Arrays.<Object> asList("a"); 
System.out.println(list1.toArray().getClass()); 

Machen Sie keine Annahmen. Verlassen Sie sich immer auf die API-Dokumentation. Wenn es nicht klar ist, versuchen Sie eine bessere Lösung zu finden.

+0

Vielen Dank für Ihre Erklärung. Ich überprüfte die Implementierung asList (T ...) in Arrays.java, es hält ein Array vom Typ T, während im Fall von ArrayList Fall Object [] verwendet wird. – hellrocker

0

Der Schlüssel hier ist, dass Arrays.asList(..) keine java.util.ArrayList zurückgibt, sondern stattdessen eine java.util.Arrays$ArrayList zurückgibt. Also die .toArray() Methoden variieren leicht.

Wenn Sie der erste Fall, dass ein String [] zurückzukehren, können Sie den Anruf ändern

str = list.toArray(new String[0]); 
1
List<String> list = new ArrayList<String>(); 
list.add("a"); 
str = (String[]) list.toArray(); 

In diesem Fall Ihre Liste Methode invoke toArray() der Klasse Arraylist. Welche wie unten aussieht, gibt es Object []:

public Object[] toArray() { 
    return Arrays.copyOf(elementData, size); 
} 

Und elementData erklären:

transient Object[] elementData; 

und Konstruktor-Methode:

public ArrayList() { 
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; 
} 

Und DEFAULTCAPACITY_EMPTY_ELEMENTDATA:

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; 

Das re für, elementData ist totaly Objekt [] und kann nicht in irgendeinen Typ, String usw. geworfen werden.

Mit Arrays.asList (T ...) gibt es java.util.Arrays $ ArrayList Klasse zurück. Und java.util.Arrays $ ArrayList hat auch toArray() -Methode. Diese subtile toArray() -Methode verwirrend :). Hier ist seine Umsetzung:

public Object[] toArray() { 
    return a.clone(); 
} 

Und schließlich ein Feld declare:

private final E[] a; 

java.util.Arrays $ ArrayList.toArray() in der Lage Objekt zurückkehren [] und tatsächlich E []. Hoffe, das wird Ihnen helfen :)

4

Die verschiedenen Aufrufe an toArray werden Arrays mit verschiedenen Komponententypen zurückgeben. Sie können dies sehen, indem Sie den folgenden Code ausführen:

List<String> list = new ArrayList<String>(); 
    list.add("a"); 
    List<String> list1 = Arrays.asList("a"); 
    System.out.println(list.toArray().getClass()); 
    System.out.println(list1.toArray().getClass()); 

Auf einer beliebigen Version von Java 8 oder früher, ist das Ergebnis

class [Ljava.lang.Object; 
    class [Ljava.lang.String; 

Grundsätzlich ist dieser Ausgabemittel Object[] und String[] sind.

Dies ist jedoch ein Fehler. Siehe JDK-6260652. Obwohl es nicht sehr klar ist, Collection.toArray()muss geben Sie eine Object[] und nicht ein Array von einigen Untertypen von Object zurück. Dafür gibt es mehrere Gründe.

Erstens wurde Collection.toArray() in JDK 1.2 eingeführt, lange bevor Generika zur Sprache hinzugefügt wurden.Es gab keine Möglichkeit, dass eine Sammlungsimplementierung irgendetwas anderes als Object[] zurücksendet. Aus Gründen der Kompatibilität müssen daher alle Implementierungen der Sammlungen toArray()Object[] zurückgeben.

Der zweite Grund ist, dass ein eher offhand Kommentar in der Spezifikation für toArray(T[]) sagt:

Beachten Sie, dass toArray (new Object [0]) in der Funktion toArray identisch ist().

die toArray() erfordert wieder Object[] und nicht eine Anordnung von einem anderen Typ zurückzukehren.

Dieser Fehler wurde in JDK wurde behoben 9. über das Code-Snippet Laufen auf einem aktuellen JDK 9 Build gibt die folgende Ausgabe:

class [Ljava.lang.Object; 
    class [Ljava.lang.Object; 

Die Tatsache, dass Arrays.asList("a") ein String[] für interne Speicher verwendet, ist eine Implementierung Detail . Der Fehler, bei dem toArray() etwas anderes als Object[] zurückgegeben hat, ist dieses Implementierungsdetail, das herausgelaufen ist. (Tatsächlich wird das Array von der varargs-Maschinerie unter Verwendung des Typparameters der Methode als Array-Komponententyp erstellt. Arrays.asList() umschließt einfach das Array.

Wie andere bereits gesagt haben, wenn Sie den Komponententyp steuern möchten von dem zurückgegebenen Array, verwenden Sie die andere Überlast toArray(T[]):

String[] array = list.toArray(new String[0]); 
    String[] array1 = list1.toArray(new String[0]); 
+1

Lustig, dass es 'toArray (T [])' ist, die dieses kritische Detail angibt. Wird die Dokumentation von 'toArray()' ebenfalls geändert? –

+1

@StiriosDelimanolis Ja, sollte es wahrscheinlich sein. Ich habe gerade [JDK-8160406] abgelegt (https://bugs.openjdk.java.net/browse/JDK-8160406). –