diferencia array java arrays arraylist

java - diferencia - Convertir ArrayList<String> a String[] array



arraylist vs list c# (6)

Esta pregunta ya tiene una respuesta aquí:

Estoy trabajando en el entorno de Android y he probado el siguiente código, pero no parece estar funcionando.

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

Si lo defino de la siguiente manera:

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

funciona. ¿Hay algo que me falta?


La forma correcta de hacer esto es:

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

Me gustaría agregar a las otras grandes respuestas aquí y explicar cómo podría haber usado los Javadocs para responder a su pregunta.

El Javadoc para toArray() (sin argumentos) está here . Como puede ver, este método devuelve un Object[] y no una String[] que es una matriz del tipo de tiempo de ejecución de su lista:

public Object[] toArray()

Devuelve una matriz que contiene todos los elementos de esta colección. Si la colección ofrece alguna garantía en cuanto al orden en que sus elementos son devueltos por su iterador, este método debe devolver los elementos en el mismo orden. La matriz devuelta será "segura", ya que la colección no mantiene ninguna referencia a ella. (En otras palabras, este método debe asignar una nueva matriz incluso si la colección está respaldada por una matriz). Por lo tanto, el llamante es libre de modificar la matriz devuelta.

Sin embargo, justo debajo de ese método está el Javadoc para toArray(T[] a) . Como puede ver, este método devuelve una T[] donde T es el tipo de matriz que pasa. Al principio, esto parece ser lo que está buscando, pero no está claro exactamente por qué está pasando una matriz (son añadiéndolo, usándolo solo para el tipo, etc). La documentación deja en claro que el propósito de la matriz pasada es esencialmente definir el tipo de matriz a devolver (que es exactamente su caso de uso):

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

Devuelve una matriz que contiene todos los elementos de esta colección; el tipo de tiempo de ejecución de la matriz devuelta es el de la matriz especificada. Si la colección se ajusta a la matriz especificada, se devuelve allí. De lo contrario, se asigna una nueva matriz con el tipo de tiempo de ejecución de la matriz especificada y el tamaño de esta colección. Si la colección se ajusta a la matriz especificada con espacio de sobra (es decir, la matriz tiene más elementos que la colección), el elemento de la matriz que sigue inmediatamente al final de la colección se establece en nulo. Esto es útil para determinar la longitud de la colección solo si la persona que llama sabe que la colección no contiene ningún elemento nulo.)

Si esta colección ofrece alguna garantía en cuanto al orden en que sus elementos son devueltos por su iterador, este método debe devolver los elementos en el mismo orden.

Esta implementación verifica si la matriz es lo suficientemente grande como para contener la colección; si no, asigna una nueva matriz del tamaño y tipo correctos (utilizando la reflexión). Luego, itera sobre la colección, almacenando cada referencia de objeto en el siguiente elemento consecutivo de la matriz, comenzando con el elemento 0. Si la matriz es más grande que la colección, se almacena un nulo en la primera ubicación después del final de la colección.

Por supuesto, se requiere una comprensión de los genéricos (como se describe en las otras respuestas) para entender realmente la diferencia entre estos dos métodos. Sin embargo, si primero va a los Javadocs, generalmente encontrará su respuesta y luego verá por sí mismo qué más necesita aprender (si realmente lo hace).

También tenga en cuenta que leer los Javadocs aquí le ayuda a comprender cuál debe ser la estructura de la matriz que pasa. Aunque puede que no importe en la práctica, no debes pasar una matriz vacía como esta:

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

Porque, desde el documento, esta implementación verifica si la matriz es lo suficientemente grande como para contener la colección; si no, asigna una nueva matriz del tamaño y tipo correctos (utilizando la reflexión). No hay necesidad de la sobrecarga adicional en la creación de una nueva matriz cuando podría pasar fácilmente el tamaño.

Como suele ser el caso, los Javadocs le proporcionan una gran cantidad de información y orientación.

Oye, espera un minuto, ¿qué es el reflejo?


Lo que está sucediendo es que stock_list.toArray() está creando un Object[] lugar de una String[] y, por lo tanto, el encasillado está fallando 1 .

El código correcto sería:

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

o incluso

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

Para obtener más detalles, consulte los javadocs para las dos sobrecargas de List.toArray .

La última versión utiliza la matriz de longitud cero para determinar el tipo de la matriz de resultados. (Sorprendentemente, es más rápido hacer esto que preasignar ... al menos, para las versiones recientes de Java. Consulte https://.com/a/4042464/139985 para obtener más información).

Desde una perspectiva técnica, la razón de este comportamiento / diseño de la API es que una implementación del método List<T>.toArray() no tiene información de lo que es <T> en tiempo de ejecución. Todo lo que sabe es que el tipo de elemento en bruto es Object . Por el contrario, en el otro caso, el parámetro de matriz proporciona el tipo base de la matriz. (Si la matriz suministrada es lo suficientemente grande como para contener los elementos de la lista, se utiliza. De lo contrario, se asigna una nueva matriz del mismo tipo y se asigna un tamaño más grande como resultado).

1 - En Java, un Object[] no es compatible con una String[] . Si lo fuera, entonces podrías hacer esto:

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

Esto es claramente una tontería, y es por eso que los tipos de arreglos generalmente no son compatibles con la asignación.


Prueba esto

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


Una alternativa en Java 8:

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


Utilice de esta manera.

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


Puedo ver muchas respuestas que muestran cómo resolver un problema, pero solo la respuesta de Stephen es tratar de explicar por qué ocurre un problema, así que trataré de agregar algo más sobre este tema. Es una historia sobre las posibles razones por las que Object[] toArray no se cambió a T[] toArray donde los genéricos se introdujeron en Java.

Por qué String[] stockArr = (String[]) stock_list.toArray(); no trabajas?

En Java, el tipo genérico existe solo en tiempo de compilación . En el tiempo de ejecución, la información sobre el tipo genérico (como en su caso <String> ) se elimina y se reemplaza por el tipo de Object (eche un vistazo a la eliminación de tipos ). Es por eso que en tiempo de ejecución toArray() no tiene idea de qué tipo preciso usar para crear una nueva matriz, por lo que usa Object como el tipo más seguro, porque cada clase extiende Objeto para que pueda almacenar de forma segura la instancia de cualquier clase.

Ahora el problema es que no puede convertir la instancia de Object[] a String[] .

¿Por qué? Eche un vistazo a este ejemplo (supongamos que la class B extends A ):

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

Aunque dicho código se compilará, en el tiempo de ejecución veremos la ClassCastException porque la instancia mantenida por la referencia a no es en realidad del tipo B (o sus subtipos). ¿Por qué es este problema (por qué esta excepción debe ser lanzada)? Una de las razones es que B podría tener nuevos métodos / campos que A no tiene, por lo que es posible que alguien intente usar estos nuevos miembros a través de la referencia b incluso si la instancia retenida no los tiene (no los admite) . En otras palabras, podríamos terminar tratando de usar datos que no existen, lo que podría llevar a muchos problemas. Por lo tanto, para evitar tal situación, JVM lanza una excepción y detiene aún más el código potencialmente peligroso.

Puede preguntar ahora "¿Por qué no nos detenemos incluso antes? ¿Por qué el código que involucra tal conversión es incluso compilable? ¿No debería el compilador detenerlo?". La respuesta es: no, porque el compilador no puede saber con certeza cuál es el tipo real de instancia que tiene a referencia, y existe la posibilidad de que contenga una instancia de clase B que admita la interfaz de b referencia. Echale un vistazo a éste ejemplo:

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

Ahora volvamos a tus arreglos. Como puede ver, no podemos convertir la instancia de la matriz Object[] en un tipo de String[] más preciso String[] como

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

Aquí el problema es un poco diferente. Ahora estamos seguros de que String[] matriz String[] no tendrá campos o métodos adicionales porque solo todas las matrices admiten:

  • [] operador,
  • length archivada,
  • Métodos heredados de supertipo de objeto,

Por lo tanto, no es una interfaz de matrices lo que lo hace imposible. El problema es que la matriz Object[] al lado de Strings puede almacenar cualquier objeto (por ejemplo, Integers ), por lo que es posible que en un hermoso día terminemos tratando de invocar un método como strArray[i].substring(1,3) en caso de Integer que no tiene dicho método.

Así que para asegurarse de que esta situación nunca suceda, en el arreglo de Java las referencias solo pueden ser válidas

  • instancias de la matriz del mismo tipo que la referencia ( String[] strArr referencia String[] strArr puede contener String[] )
  • instancias de matriz de subtipo ( Object[] puede contener String[] porque String es subtipo de Object ),

pero no puedo sostener

  • matriz de supertipo de tipo de matriz de referencia ( String[] no puede contener Object[] )
  • matriz de tipo que no está relacionada con el tipo de referencia (el Integer[] no puede contener la String[] )

En otras palabras, algo como esto está bien.

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

Podría decir que una forma de resolver este problema es encontrar en el tiempo de ejecución el tipo más común entre todos los elementos de la lista y crear una matriz de ese tipo, pero esto no funcionará en situaciones donde todos los elementos de la lista serán de un tipo derivado de uno genérico. Echar un vistazo

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

ahora el tipo más común es B , no A por lo que toArray()

A[] arr = elements.toArray();

devolvería la matriz de la clase new B[] . El problema con esta matriz es que si bien el compilador le permitiría editar su contenido agregándole un new A() elemento new A() , obtendría ArrayStoreException porque la matriz B[] solo puede contener elementos de la clase B o su subclase, para asegurarse de que todos los elementos admitirán la interfaz de B , pero la instancia de A puede no tener todos los métodos / campos de B Así que esta solución no es perfecta.

La mejor solución para este problema es explícitamente decir qué tipo de matriz toArray() debe devolverse pasando este tipo como argumento de método como

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

o

String[] arr = list.toArray(new String[0]); //if size of array is smaller then list it will be automatically adjusted.