¿Qué es boxeo y unboxing y cuáles son las compensaciones?

Estoy buscando una respuesta clara, concisa y precisa.

Idealmente como la respuesta real, aunque los enlaces a buenas explicaciones son bienvenidos.

Los valores encasillados son estructuras de datos que son envoltorios mínimos alrededor de tipos primitivos *. Los valores encasillados normalmente se almacenan como punteros a objetos en el montón .

Por lo tanto, los valores encuadrados usan más memoria y toman como mínimo dos búsquedas de memoria para acceder: una para obtener el puntero y otra para seguir ese puntero a la primitiva. Obviamente, este no es el tipo de cosa que quieres en tus circuitos internos. Por otro lado, los valores encuadrados suelen jugar mejor con otros tipos en el sistema. Como son estructuras de datos de primera clase en el lenguaje, tienen los metadatos y la estructura esperados que tienen otras estructuras de datos.

En Java y Haskell, las colecciones genéricas no pueden contener valores no compartidos. Las colecciones genéricas en .NET pueden contener valores desempacados sin penalizaciones. Cuando los generics de Java solo se utilizan para la verificación de tipos en tiempo de comstackción, .NET generará clases específicas para cada tipo genérico instanciado en tiempo de ejecución .

Java y Haskell tienen matrices unboxed, pero son claramente menos convenientes que las otras colecciones. Sin embargo, cuando se necesita un rendimiento máximo, vale la pena un pequeño inconveniente para evitar la sobrecarga del boxeo y el desempaquetado.

* Para esta discusión, un valor primitivo es cualquiera que se pueda almacenar en la stack de llamadas , en lugar de almacenarse como un puntero a un valor en el montón. Con frecuencia, son solo los tipos de máquina (ints, flotantes, etc.), las estructuras y, en ocasiones, las matrices de tamaño estático. .NET-land los llama tipos de valores (a diferencia de los tipos de referencia). Los tipos de Java los llaman tipos primitivos. Haskellions simplemente los llama desagrupados.

** También me estoy enfocando en Java, Haskell y C # en esta respuesta, porque eso es lo que sé. Por lo que vale, Python, Ruby y Javascript tienen valores enmarcados exclusivamente. Esto también se conoce como el enfoque “Todo es un objeto” ***.

*** Advertencia: Un comstackdor / JIT suficientemente avanzado puede, en algunos casos, detectar realmente que un valor semánticamente encerrado al mirar la fuente puede ser un valor no empaquetado en tiempo de ejecución. En esencia, gracias a los implementadores de idiomas shinys, sus cajas son a veces gratuitas.

de C # 3.0 en pocas palabras :

El boxeo es el acto de convertir un tipo de valor en un tipo de referencia:

 int x = 9; object o = x; // boxing the int 

unboxing es … al revés:

 // unboxing o object o = 9; int x = (int)o; 

El boxeo y el desempaquetado es el proceso de convertir un valor primitivo en una clase contenedora orientada a objetos (boxeo), o convertir un valor de una clase contenedora orientada a objetos al valor primitivo (unboxing).

Por ejemplo, en java, puede necesitar convertir un valor int en un Integer (boxeo) si desea almacenarlo en una Collection porque las primitivas no pueden almacenarse en una Collection , solo objetos. Pero cuando desee recuperarlo de la Collection , puede querer obtener el valor como int y no como Integer para que pueda desempaquetarlo.

El boxeo y el desempaquetado no es inherentemente malo , pero es una compensación. Dependiendo de la implementación del idioma, puede ser más lento y requiere más memoria que el simple uso de primitivas. Sin embargo, también puede permitirle usar estructuras de datos de mayor nivel y lograr una mayor flexibilidad en su código.

En la actualidad, se debate con más frecuencia en el contexto de la característica “autoboxing / autounboxing” de Java (y de otros idiomas). Aquí hay una explicación centrada en Java del autoboxing .

En la red:

A menudo no puede confiar en qué tipo de variable consumirá una función, por lo que necesita usar una variable de objeto que se extienda desde el denominador común más bajo: en .Net, este es un object .

Sin embargo, el object es una clase y almacena sus contenidos como referencia.

 List notBoxed = new List { 1, 2, 3 }; int i = notBoxed[1]; // this is the actual value List boxed = new List { 1, 2, 3 }; int j = (int) boxed[1]; // this is an object that can be 'unboxed' to an int 

Si bien ambos contienen la misma información, la segunda lista es más grande y más lenta. Cada valor en la segunda lista es en realidad una referencia a un object que contiene el int .

Esto se llama boxed porque el int está envuelto por el object . Cuando se rechaza, int se desempaqueta, se vuelve a convertir en su valor.

Para los tipos de valor (es decir, todas las structs ), esto es lento y potencialmente utiliza mucho más espacio.

Para los tipos de referencia (es decir, todas las classes ), este es un problema mucho menor, ya que se almacenan como referencia de todos modos.

Otro problema con el tipo de valor encuadrado es que no es obvio que se trata de la caja, en lugar del valor. Cuando comparas dos structs entonces estás comparando valores, pero cuando comparas dos classes entonces (de forma predeterminada) estás comparando la referencia, es decir, ¿estas son la misma instancia?

Esto puede ser confuso cuando se trata de tipos de valores encuadrados:

 int a = 7; int b = 7; if(a == b) // Evaluates to true, because a and b have the same value object c = (object) 7; object d = (object) 7; if(c == d) // Evaluates to false, because c and d are different instances 

Es fácil evitarlo:

 if(c.Equals(d)) // Evaluates to true because it calls the underlying int's equals if(((int) c) == ((int) d)) // Evaluates to true once the values are cast 

Sin embargo, es otra cosa a tener cuidado cuando se trata de valores en caja.

Las colecciones genéricas de .NET FCL:

 List Dictionary SortedDictionary Stack Queue LinkedList 

Todos fueron diseñados para superar los problemas de rendimiento del boxeo y el desempaquetado en implementaciones de colecciones anteriores.

Para más información, vea el capítulo 16, CLR a través de C # (2nd Edition) .

El boxeo es el proceso de conversión de un tipo de valor en un tipo de referencia.

Unboxing es la conversión de un tipo de referencia en un tipo de valor.

 EX: int i=123; object o=i;// Boxing int j=(int)o;// UnBoxing 

Tipo de valor son:
int, char y estructuras, enumeraciones. Tipo de referencia son: Clases, interfaces, matrices, cadenas y objetos

El boxeo y el desempaquetado facilita que los tipos de valor sean tratados como objetos. Boxeo significa convertir un valor a una instancia del tipo de referencia del objeto. Por ejemplo, Int es una clase e int es un tipo de datos. La conversión de int a Int es una ejemplificación del boxeo, mientras que la conversión de Int a int es unboxing. El concepto ayuda en la recolección de basura, Unboxing, por otro lado, convierte el tipo de objeto a tipo de valor.

 int i=123; object o=(object)i; //Boxing o=123; i=(int)o; //Unboxing. 

Como cualquier otra cosa, el autoboxing puede ser problemático si no se usa con cuidado. El clásico es terminar con una NullPointerException y no ser capaz de rastrearla. Incluso con un depurador. Prueba esto:

 public class TestAutoboxNPE { public static void main(String[] args) { Integer i = null; // .. do some other stuff and forget to initialise i i = addOne(i); // Whoa! NPE! } public static int addOne(int i) { return i + 1; } }