Buscar..


Introducción

Autoboxing es la conversión automática que realiza el compilador de Java entre los tipos primitivos y sus correspondientes clases de envoltorios de objetos. Ejemplo, conversión de int -> Entero, doble -> Doble ... Si la conversión es a la inversa, esto se llama unboxing. Por lo general, esto se usa en Colecciones que no pueden contener más que Objetos, donde se necesitan tipos primitivos de boxeo antes de configurarlos en la colección.

Observaciones

Autoboxing puede tener problemas de rendimiento cuando se utiliza con frecuencia en su código.

Usando int y entero indistintamente

A medida que usa tipos genéricos con clases de utilidad, a menudo puede encontrar que los tipos de números no son muy útiles cuando se especifican como tipos de objetos, ya que no son iguales a sus contrapartes primitivas.

List<Integer> ints = new ArrayList<Integer>();
Java SE 7
List<Integer> ints = new ArrayList<>();

Afortunadamente, las expresiones que evalúan a int pueden usarse en lugar de un Integer cuando sea necesario.

for (int i = 0; i < 10; i++)
    ints.add(i);

El ints.add(i); declaración es equivalente a:

ints.add(Integer.valueOf(i));

Y retiene las propiedades de Integer#valueOf , como tener los mismos objetos de Integer almacenados en caché por la JVM cuando está dentro del rango de almacenamiento en caché de números.

Esto también se aplica a:

  • byte y Byte
  • short y Short
  • float y Float
  • double y Double
  • long y Long
  • char y Character
  • boolean y Boolean

Se debe tener cuidado, sin embargo, en situaciones ambiguas. Considere el siguiente código:

List<Integer> ints = new ArrayList<Integer>();
ints.add(1);
ints.add(2);
ints.add(3);
ints.remove(1); // ints is now [1, 3]

La interfaz java.util.List contiene tanto un remove(int index) (Método de interfaz de List ) como un remove(Object o) (método heredado de java.util.Collection ). En este caso no se lleva a cabo ningún boxeo y se llama a la remove(int index) .

Un ejemplo más del extraño comportamiento del código Java causado por los enteros automoxing con valores en el rango de -128 a 127 :

Integer a = 127;
Integer b = 127;
Integer c = 128;
Integer d = 128;
System.out.println(a == b); // true
System.out.println(c <= d); // true
System.out.println(c >= d); // true
System.out.println(c == d); // false

Esto sucede porque el operador >= llama implícitamente a intValue() que devuelve int mientras que == compara las referencias , no los valores de int .

De forma predeterminada, Java almacena en caché los valores en el rango [-128, 127] , por lo que el operador == funciona porque los Integers en este rango hacen referencia a los mismos objetos si sus valores son los mismos. El valor máximo del rango -XX:AutoBoxCacheMax se puede definir con la -XX:AutoBoxCacheMax JVM. Por lo tanto, si ejecuta el programa con -XX:AutoBoxCacheMax=1000 , el siguiente código se imprimirá true :

Integer a = 1000;
Integer b = 1000;
System.out.println(a == b); // true

Usando Boolean en la sentencia if

Debido al auto-boxeo, uno puede usar un Boolean en una sentencia if :

Boolean a = Boolean.TRUE;
if (a) { // a gets converted to boolean
    System.out.println("It works!");
}

Eso funciona para while , do while y la condición en las declaraciones for también.

Tenga en cuenta que, si el valor Boolean es null , se lanzará una NullPointerException en la conversión.

Auto-unboxing puede llevar a NullPointerException

Este código compila:

Integer arg = null;
int x = arg;

Pero se bloqueará en tiempo de ejecución con una java.lang.NullPointerException en la segunda línea.

El problema es que un int primitivo no puede tener un valor null .

Este es un ejemplo minimalista, pero en la práctica a menudo se manifiesta en formas más sofisticadas. La NullPointerException no es muy intuitiva y suele ser de poca ayuda para localizar estos errores.

Confíe en el autoboxing y el auto-boxeo con cuidado, asegúrese de que los valores unboxed no tengan valores null en el tiempo de ejecución.

Memoria y sobrecarga computacional de Autoboxing

Autoboxing puede venir en una sobrecarga de memoria sustancial. Por ejemplo:

Map<Integer, Integer> square = new HashMap<Integer, Integer>();
for(int i = 256; i < 1024; i++) {
    square.put(i, i * i); // Autoboxing of large integers
}

normalmente consumirá una cantidad sustancial de memoria (alrededor de 60kb para 6k de datos reales).

Además, los enteros en caja generalmente requieren viajes de ida y vuelta adicionales en la memoria, y por lo tanto hacen que los cachés de CPU sean menos efectivos. En el ejemplo anterior, la memoria a la que se accede se extiende a cinco ubicaciones diferentes que pueden estar en regiones completamente diferentes de la memoria: 1. el objeto HashMap , 2. el objeto de Entry[] table del mapa, 3. el objeto Entry , 4. el objeto objeto key entradas (boxeo de la clave primitiva), 5. objeto de value entradas (boxeo del valor primitivo).

class Example {
  int primitive; // Stored directly in the class `Example`
  Integer boxed; // Reference to another memory location
}

La lectura en boxed requiere dos accesos de memoria, accediendo solo a uno primitive .

Al obtener datos de este mapa, el código aparentemente inocente

int sumOfSquares = 0;
for(int i = 256; i < 1024; i++) {
    sumOfSquares += square.get(i);
}

es equivalente a:

int sumOfSquares = 0;
for(int i = 256; i < 1024; i++) {
    sumOfSquares += square.get(Integer.valueOf(i)).intValue();
}

Normalmente, el código anterior causa la creación y recolección de elementos no utilizados de un objeto Integer para cada operación Map#get(Integer) . (Vea la Nota a continuación para más detalles).

Para reducir esta sobrecarga, varias bibliotecas ofrecen colecciones optimizadas para tipos primitivos que no requieren boxeo. Además de evitar la sobrecarga del boxeo, esta colección requerirá aproximadamente 4 veces menos memoria por entrada. Si bien Java Hotspot puede optimizar el autoboxing al trabajar con objetos en la pila en lugar del montón, no es posible optimizar la sobrecarga de memoria y la indirección de la memoria resultante.

Las transmisiones Java 8 también tienen interfaces optimizadas para tipos de datos primitivos, como IntStream que no requieren boxeo.

Nota: un tiempo de ejecución de Java típico mantiene un caché simple de Integer y otro objeto de envoltorio primitivo que utilizan los métodos de fábrica valueOf , y mediante autofijación. Para Integer , el rango predeterminado de este caché es de -128 a +127. Algunas JVM proporcionan una opción de línea de comandos JVM para cambiar el tamaño / rango de la memoria caché.

Casos diferentes cuando Integer e int pueden usarse indistintamente

Caso 1: Mientras se usa en lugar de argumentos de método.

Si un método requiere un objeto de la clase envoltura como argumento. Luego, indistintamente, se le puede pasar una variable del tipo primitivo respectivo y viceversa.

Ejemplo:

int i;
Integer j;
void ex_method(Integer i)//Is a valid statement
void ex_method1(int j)//Is a valid statement

Caso 2: Mientras se pasan valores de retorno:

Cuando un método devuelve una variable de tipo primitivo, entonces se puede pasar un objeto de la clase envoltura correspondiente como valor de retorno de manera intercambiable y viceversa.

Ejemplo:

int i;
Integer j;
int ex_method()
{...
return j;}//Is a valid statement
Integer ex_method1()
{...
return i;//Is a valid statement
}

Caso 3: Al realizar operaciones.

Siempre que se realicen operaciones en números, la variable de tipo primitivo y el objeto de la respectiva clase envoltura se pueden usar indistintamente.

int i=5;
Integer j=new Integer(7);
int k=i+j;//Is a valid statement
Integer m=i+j;//Is also a valid statement

Trampa : recuerde inicializar o asignar un valor a un objeto de la clase contenedora.

Si bien el uso de un objeto de la clase de envoltorio y la variable primitiva no se olvida, nunca se olvida o se pierde para inicializar o asignar un valor al objeto de la clase de envoltorio, de lo contrario, puede provocar una excepción de puntero nulo en el tiempo de ejecución.

Ejemplo:

public class Test{
    Integer i;
    int j;
    public void met()
    {j=i;//Null pointer exception
    SOP(j);
    SOP(i);}   
    public static void main(String[] args)
    {Test t=new Test();
    t.go();//Null pointer exception
    }

En el ejemplo anterior, el valor del objeto no está asignado ni inicializado y, por lo tanto, en tiempo de ejecución, el programa ejecutará una excepción de puntero nulo. Por lo tanto, el valor del objeto nunca debe dejarse sin inicializar ni asignar.



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow