java arrays list variadic-functions

java - Arrays.asList() no funciona como debería?



variadic-functions (9)

¿Por qué el autoboxing no funciona aquí? es decir, int [] a entero []?

Mientras que el autoboxing convertirá un int a un Integer , no convertirá un int[] en un Integer[] .

Por qué no?

La respuesta simple (pero insatisfactoria) es porque eso es lo que dice el JLS. (Puede verificarlo si lo desea).

La respuesta real es fundamental para lo que hace el autoboxing y por qué es seguro.

Cuando autobox 1 en cualquier parte de su código, obtiene el mismo objeto Integer . Esto no es cierto para todos los valores int (debido al tamaño limitado de la caché de autoboxeo Integer ), pero si usa los equals para comparar objetos Integer obtendrá la respuesta "correcta".

Básicamente, N == N siempre es verdadero y el new Integer(N).equals(new Integer(N)) siempre es verdadero. Además, estas dos cosas siguen siendo ciertas ... suponiendo que te quedas con el código Pure Java.

Ahora considera esto:

int[] x = new int[]{1}; int[] y = new int[]{1};

¿Son iguales? ¡No! x == y es falso y x.equals(y) es falso! ¿Pero por qué? Porque:

y[0] = 2;

En otras palabras, dos matrices con el mismo tipo, tamaño y contenido siempre se distinguen porque las matrices de Java son mutables.

La "promesa" de autoboxing es que está bien hacer porque los resultados son indistinguibles 1 . Pero, debido a que todas las matrices son fundamentalmente distinguibles debido a la definición de equals para matrices Y matriz de mutabilidad. Entonces, si se permitiera el autoboxing de matrices de tipos primitivos, socavaría la "promesa".

1 - ..... siempre que no use == para probar si los valores de autoboxed son iguales.

Tengo un flotador [] y me gustaría obtener una lista con los mismos elementos. Podría hacer lo feo de agregarlos uno por uno, pero quería usar el método Arrays.asList. Sin embargo hay un problema. Esto funciona:

List<Integer> list = Arrays.asList(1,2,3,4,5);

Pero esto no.

int[] ints = new int[] {1,2,3,4,5}; List<Integer> list = Arrays.asList(ints);

El método asList acepta un parámetro varargs que, según mi conocimiento, es una "taquigrafía" para una matriz.

Preguntas:

  • ¿Por qué la segunda parte del código devuelve una List<int[]> pero no una List<int> ?

  • ¿Hay alguna forma de corregirlo?

  • ¿Por qué el autoboxing no funciona aquí? es decir, int[] a Integer[] ?


¿Qué tal esto?

Integer[] ints = new Integer[] {1,2,3,4,5}; List<Integer> list = Arrays.asList(ints);


Debido a que las matrices Java son objetos y Arrays.asList() trata su matriz int como un único argumento en la lista varargs.


El problema no está en Arrays.asList() . El problema es que esperas que el autoboxing funcione en una matriz, y no es así. En el primer caso, el compilador autocaptura las entradas individuales antes de ver en qué se usan. En el segundo caso, primero los coloca en una matriz int (no es necesario el autoboxing) y luego los pasa a Arrays.asList() (no es posible el autoboxing).


Ingrese Java 8, y puede hacer lo siguiente para recopilar en una matriz en caja:

Integer[] boxedInts = IntStream.of(ints).boxed().toArray(Integer[]::new);

O esto para recopilar en una lista en caja

List<Integer> boxedInts = IntStream.of(ints).boxed().collect(Collectors.toList());

Sin embargo, esto solo funciona para int[] , long[] y double[] . Esto no funcionará para byte[] .

Tenga en cuenta que Arrays.stream(ints) y IntStream.of(ints) son equivalentes. Entonces, dos ejemplos anteriores también pueden reescribirse como:

Integer[] boxedIntArray = Arrays.stream(ints).boxed().toArray(Integer[]::new); List<Integer> boxedIntList = Arrays.stream(ints).boxed().collect(Collectors.toList());

Esta última forma podría ser favorecida ya que omite un subtipo primitivo específico de Stream . Sin embargo, internamente todavía hay un montón de sobrecargas que en este caso aún crean un IntStream internamente.


No es posible convertir int[] a Integer[] , debe copiar los valores

int[] tab = new int[]{1, 2, 3, 4, 5}; List<Integer> list = ArraysHelper.asList(tab);

public static List<Integer> asList(int[] a) { List<Integer> list = new ArrayList<Integer>(); for (int i = 0; i < a.length && list.add(a[i]); i++); return list; }


No existe una List<int> en Java: los genéricos no admiten primitivas.

El autoboxing solo ocurre para un solo elemento, no para matrices de primitivas.

En cuanto a cómo corregirlo, hay varias bibliotecas con montones de métodos para hacer cosas como esta. No hay forma de evitar esto, y no creo que haya nada que lo haga más fácil dentro del JDK. Algunos envolverán una matriz primitiva en una lista del tipo de envoltura (para que el boxeo ocurra en el acceso), otros iterarán a través de la matriz original para crear una copia independiente, a medida que avanzan. Asegúrate de saber cuál estás usando.

(EDIT: he estado suponiendo que el punto de partida de una int[] no era negociable. Si puedes comenzar con un Integer[] entonces estás muy lejos :)

Solo por un ejemplo de una biblioteca auxiliar, y para conectar un poco a Guava , está com.google.common.primitive.Ints.asList .


Si pasa un int[] a Arrays.asList() , la lista creada será List<int[]> , que no es vaild en java, no es la List<Integer> correcta List<Integer> .

Creo que estás esperando que Arrays.asList() auto-box tus entradas, que como has visto, no lo hará.


Arrays.asList(T... a) toma efectivamente una T[] que coincidirá con cualquier matriz de objetos verdaderos (subclases de Object ) como una matriz. Lo único que no coincidirá con eso es una matriz de primitivas, ya que los tipos primitivos no se derivan de Object . Entonces, un int[] no es un Object[] .

Lo que sucede entonces es que el mecanismo de varags entra en acción y lo trata como si hubiera pasado un solo objeto, y crea una única matriz de elementos de ese tipo. Así que pasa un int[][] (aquí, T es int[] ) y termina con una lista de 1 elemento List<int[]> que no es lo que quiere.

Aún tienes algunas opciones bastante buenas:

Int.asList(int[]) Guava

Si su proyecto ya usa guayaba, es tan simple como usar el adaptador que Guava proporciona: Int.asList() . Hay un adaptador similar para cada tipo primitivo en la clase asociada, por ejemplo, Booleans para boolean , etc.

int foo[] = {1,2,3,4,5}; Iterable<Integer> fooBar = Ints.asList(foo); for(Integer i : fooBar) { System.out.println(i); }

La ventaja de este enfoque es que crea una envoltura delgada alrededor de la matriz existente, por lo que la creación de la envoltura es de tiempo constante (no depende del tamaño de la matriz) y el almacenamiento requerido es solo una pequeña cantidad constante ( menos de 100 bytes) además de la matriz entera subyacente.

La desventaja es que el acceso a cada elemento requiere una operación de boxing de la int subyacente, y la configuración requiere unboxing. Esto puede ocasionar una gran cantidad de asignación de memoria transitoria si accede a la lista en gran medida. Si accede a cada objeto muchas veces en promedio, puede ser mejor usar una implementación que encasille los objetos una vez y los almacene como Integer . La solución a continuación hace eso.

Java 8 IntStream

En Java 8, puede usar el Arrays.stream(int[]) para convertir una matriz int en una Stream . Dependiendo de su caso de uso, es posible que pueda usar la transmisión directamente, por ejemplo, para hacer algo con cada elemento con forEach(IntConsumer) . En ese caso, esta solución es muy rápida y no incluye ningún boxeo o unboxing, y no crea ninguna copia de la matriz subyacente.

Alternativamente, si realmente necesita un List<Integer> , puede usar stream.boxed().collect(Collectors.toList()) como se sugiere aquí . La desventaja de este enfoque es que recubre por completo cada elemento de la lista, lo que podría aumentar su huella de memoria casi en un orden de magnitud, creando un nuevo Object[] para contener todos los elementos enmarcados. Si posteriormente utiliza la lista con fuerza y ​​necesita objetos Integer lugar de int , esto puede dar sus frutos, pero es algo a tener en cuenta.