2008-08-16 16 views
115

Estoy buscando una respuesta clara, concisa y precisa.¿Qué es boxeo y desembalaje y cuáles son las compensaciones?

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

+2

¿Esto realmente es independiente del idioma? –

+3

@HenkHolterman ciertamente no es específico de un idioma, aunque tampoco es relevante para _todos los idiomas; la distinción será irrelevante para la mayoría de los lenguajes de tipeo dinámico, por ejemplo. No estoy seguro de qué etiqueta podría usarse en su lugar: 'language-but-not-type-agnostic'? 'static-language-agnostic'? No estoy seguro de que SO necesite la distinción; podría ser una buena pregunta para meta. – Keith

Respuesta

170

Los valores en caja son data structures que son envoltorios mínimos alrededor de primitive types *. Los valores encasillados normalmente se almacenan como punteros a objetos en the heap.

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 las colecciones genéricas de Java y Haskell no pueden contener los valores desempacados. Las colecciones genéricas en .NET pueden contener valores desempacados sin penalizaciones. Cuando los genéricos de Java solo se usan para la verificación de tipos en tiempo de compilación, .NET mostrará generate specific classes for each generic type instantiated at run time.

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 the call stack, 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 compilador/JIT lo suficientemente avanzado puede en algunos casos realmente detectar que un valor que está 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 brillantes, sus cajas son a veces gratuitas.

+0

¿Por qué, aunque un valor enmarcado, qué beneficio obtiene el CLR o lo que sea como valores de boxeo? – PositiveGuy

+0

En resumen (ja ja), son solo otro Objeto, que es muy conveniente. Los primitivos (al menos en Java) no descienden de Object, no pueden tener campos, no pueden tener métodos, y en general se comportan de forma muy diferente a otros tipos de valores. Por otro lado, trabajar con ellos puede ser muy rápido y eficiente en el uso del espacio. Por lo tanto, el intercambio. –

+1

Javascript ha llamado a las matrices tipadas (nuevo UInt32Array, etc.) que son matrices de ints y flotantes sin caja. – nponeccop

66

boxeo & unboxing 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, es posible que necesite convertir un valor int en un Integer (boxeo) si desea almacenarla en un Collection porque primitivas no se pueden almacenar en un Collection, sólo los objetos. Pero cuando desee recuperarlo del Collection, puede querer obtener el valor como int y no como Integer para que pueda desempaquetarlo.

El boxeo y el desempaquetado no es intrínsecamente 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 discute más comúnmente en el contexto de la característica "autoboxing/autounboxing" de Java (y de otros idiomas). Aquí hay un java centric explanation of autoboxing.

23

En .Net:

A menudo no se puede confiar en lo que el tipo de variable en función va a consumir, por lo que es necesario utilizar una variable de objeto que se extiende desde el mínimo común denominador - en .Net esto se object. Sin embargo, object es una clase y almacena sus contenidos como referencia.

List<int> notBoxed = new List<int> { 1, 2, 3 }; 
int i = notBoxed[1]; // this is the actual value 

List<object> boxed = new List<object> { 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 de la segunda lista es en realidad una referencia a un object que sostiene el int.

Esto se llama en caja porque el int está envuelto por el object. Cuando se rechazó, el int se desempaquetó - se volvió a convertir en su valor.

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

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

Un problema adicional con un tipo de valor en caja es que es obvio que no se trata con la caja, en lugar del valor. Cuando se comparan dos structs entonces usted está comparando los valores, pero cuando se compara dos classes entonces (por defecto) que está comparando la referencia - es decir, son éstos la misma instancia?

Esto puede ser confuso cuando se trata de los tipos de valor en caja:

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 de solucionar:

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 que tener cuidado de cuando se trata de valores en caja.

+1

En vb.net, la distinción entre semántica de igualdad es más clara, 'Object' no implementa el operador de igualdad, pero los tipos de clase se pueden comparar con el operador' Is'; por el contrario, 'Int32' se puede usar con el operador de igualdad, pero no con' Is'. Esa distinción deja mucho más claro qué tipo de comparación se está haciendo. – supercat

110

de C# 3.0 In a Nutshell:

boxeo es el acto de emitir un tipo de valor en un tipo de referencia:

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

unboxing es ... la inversa:

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

+1, ahora ESO es ** conciso **. – Sam

3

El .NET FCL colecciones genéricas:

List<T> 
Dictionary<TKey, UValue> 
SortedDictionary<TKey, UValue> 
Stack<T> 
Queue<T> 
LinkedList<T> 

fueron diseñados para superar los problemas de rendimiento de boxeo y unboxing en las implementaciones de recolección anteriores.

Para obtener más información, consulte el capítulo 16, CLR via C# (2nd Edition).

-2

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. Pruebe 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; 
    } 
} 
+0

Esto es solo un código incorrecto, y no tiene nada que ver con el autoboxing. La variable 'i' se inicializa prematuramente. O conviértalo en una declaración vacía ('Integer i;') para que el compilador pueda señalar que olvidó inicializarlo o esperar a declararlo hasta que sepa su valor. – erickson

+0

Hmm, y si hago algo entremedio dentro de un bloque try catch, entonces el compilador me obligará a inicializarlo con algo. Esto no es un código real, es un ejemplo de cómo podría suceder. – fiddlesticks

+0

¿Qué demuestra esto? No hay absolutamente ninguna razón para usar el objeto Integer. En cambio, ahora debes lidiar con un NullPointer potencial. –

1

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 Valor son:
int, char y estructuras, enumeraciones. Tipo de referencia son: Clases, interfaces, matrices, cadenas y objetos

0

El boxeo y el desempaquetado permiten que los tipos de valores sean tratados como objetos. Boxeo significa convertir un valor a una instancia del tipo de referencia del objeto. Por ejemplo, Int es una clase y 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. 
Cuestiones relacionadas