2016-06-24 30 views
7

Stavo lavorando su framework Collection in Java, dove ho riscontrato uno strano problema. Ho creato 2 elenchi di stringhe 1 con l'aiuto di ArrayList mentre il secondo è stato creato utilizzando Arrays.asList (T ...).Comportamento diverso durante la conversione di un elenco di stringhe in un array di stringhe

Dopo la creazione di questi due lista ho cercato di convertire queste liste in matrici di stringhe con la list.toArray(),
come list.toArray() chiamata al metodo restituisce un array oggetto, quindi ho dovuto lanciare esplicitamente String [].

Dopo la fusione qualche comportamento strano sta accadendo come:

Caso # 1: (in cui la lista è stata creata usando ArrayList), dà eccezione di runtime come java.lang.ClassCastException: [Ljava.lang.Object; non può essere lanciato su [Ljava.lang.String;

Caso 2: (dove l'elenco creato utilizzando Arrays.asList (T ...)) funziona correttamente.

Ecco il codice

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 

risposta

7

Un ArrayList è supportato da un Object[]. Una copia di tale array viene restituita con toArray().

Restituisce una matrice contenente tutti gli elementi in questo elenco nella sequenza corretta (dal primo all'ultimo elemento).

Non fornisce alcuna garanzia sul tipo di matrice restituita. Ma lo sappiamo dal messaggio dell'eccezione. Se si desidera che restituisca un String[], utilizzare il metodo sovraccaricato fornito per questo motivo.

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

Il cast non è necessario.

L'implementazione List restituita da Arrays.asList mantiene un riferimento all'array (implicito o esplicito) passato come argomento arity variabile.

Restituisce un elenco di dimensioni fisse supportato dall'array specificato.

L'invocazione

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

crea un String[] e passa che come argomento di asList. Questo non è specificato chiaramente dal documentario API, ma supportato da sembra indicare che restituirà lo stesso array. (Osservando l'implementazione, restituisce un clone.) Poiché si tratta di un valore String[], non vi è alcun errore durante il casting e l'assegnazione a una variabile di quel tipo.


In entrambi i casi, la soluzione appropriata è utilizzare il sovraccarico List#toArray(T[]).


Per divertimento, eseguire quanto segue e verificare il tipo di array restituito.

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

Non fare ipotesi. Fai sempre affidamento sulla documentazione dell'API. Se non è chiaro, prova a trovare una soluzione migliore.

+0

Grazie mille per la spiegazione. Ho controllato l'implementazione asList (T ...) in Arrays.java, mantiene una matrice di tipo T, mentre nel caso di ArrayList viene utilizzato l'oggetto []. – hellrocker

0

La chiave qui è che Arrays.asList(..) non restituisce un java.util.ArrayList, ma invece restituisce un java.util.Arrays$ArrayList. Quindi i metodi .toArray() variano leggermente.

Se si desidera che il primo caso per restituire una stringa [], è possibile modificare la chiamata a

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

In questo caso il metodo di lista invoke toArray() della classe ArrayList. Che si presenta come di seguito, restituisce Object []:

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

E elementData dichiarano:

transient Object[] elementData; 

E metodo di costruzione:

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

E DEFAULTCAPACITY_EMPTY_ELEMENTDATA:

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; 

Il re per, elementData è Totaly Object [] e non può essere castato a nessun tipo, String ecc ...

Con Arrays.asList (T ...), restituisce classe java.util.Array $ ArrayList. E java.util.Arrays $ ArrayList ha anche il metodo toArray(). Quel sottile metodo toArray() fa confondere :). Ecco la sua attuazione:

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

E infine una dichiarazione campo:

private final E[] a; 

java.util.Arrays $ ArrayList.toArray() in grado di restituire Object [] e in realtà E []. Spero che questo ti sia d'aiuto :)

4

Le diverse chiamate a toArray restituiscono array con tipi di componenti diversi. Si può vedere questo eseguendo il seguente codice:

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

su qualsiasi versione di Java 8 o precedente, il risultato è

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

Fondamentalmente questa uscita significa Object[] e String[] rispettivamente.

Tuttavia, questo è un bug. Vedi JDK-6260652. Anche se non è indicato molto chiaramente, Collection.toArray()deve restituire un Object[] e non un array di alcuni sottotipi di Object. Ci sono un paio di ragioni per questo.

Il primo è che Collection.toArray() è stato introdotto in JDK 1.2, molto prima che i generici fossero aggiunti alla lingua.Non c'era alcuna possibilità che l'implementazione di una raccolta restituisse qualcosa di diverso da Object[], quindi per la compatibilità, le implementazioni di tutte le raccolte 'toArray() devono restituire Object[].

La seconda ragione è che un commento piuttosto estemporaneo nella specifica per toArray(T[]) dice:

noti che toArray (new Object [0]) è identico in funzione toArray().

che richiede nuovamente toArray() per tornare Object[] e non un array di altro tipo.

Questo errore è stato corretto in JDK 9. L'esecuzione del codice frammento di codice su un recente JDK 9 accumulo pronunciato la seguente output:

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

Il fatto che Arrays.asList("a") utilizza un String[] di memorizzazione interna è un dettaglio di implementazione . Il bug in cui toArray() ha restituito qualcosa di diverso da Object[] è la fuoriuscita di questo dettaglio di implementazione. (Infatti, la matrice viene creata dal macchinario varargs, utilizzando il parametro di tipo del metodo come tipo di componente dell'array. Arrays.asList() avvolge semplicemente l'array fornito.)

Come altri hanno detto, se si desidera controllare il tipo di componente dell'array restituito, utilizzare l'altro sovraccarico toArray(T[]):

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

Divertente che sia 'toArray (T [])' che specifica questo dettaglio critico. Anche la documentazione di 'toArray()' sarà modificata? –

+1

@SotiriosDelimanolis Sì, probabilmente dovrebbe essere. Ho appena archiviato [JDK-8160406] (https://bugs.openjdk.java.net/browse/JDK-8160406). –

Problemi correlati