什么是拳击和拆箱,什么是折衷?


115

我正在寻找一个清晰,简明和准确的答案。

理想情况下,作为实际的答案,虽然链接到良好的解释欢迎。

+2

这真的是语言不可知的吗? 29 10月. 122012-10-29 12:51:04

+3

@HenkHolterman它当然不是特定于语言的,尽管它也与_all_语言无关 - 例如,对于大多数动态类型的语言,这种区别将是无关紧要的。我不确定可以使用什么标签 - “语言 - 但不是类型不可知的”? '静态语言agnostic'?我不确定这是否需要区分;虽然可能是一个很好的问题。 29 10月. 122012-10-29 13:08:21

170

盒装值为data structures,这是primitive types *附近的最小包装。盒装值通常存储为指向the heap上的对象的指针。

因此,装箱值使用更多的内存,并至少需要两次内存查找才能访问:一次获取指针,另一次访问指向该原始指针。显然这不是你内心循环中想要的东西。另一方面,盒装值通常在系统中的其他类型中表现更好。由于它们是该语言中的一流数据结构,因此它们具有其他数据结构所具有的预期元数据和结构。

在Java和Haskell通用集合中不能包含取消装箱的值。 .NET中的通用集合可以保留无装箱值而不受任何处罚。在Java的泛型只用于编译时类型检查的地方,.NET将generate specific classes for each generic type instantiated at run time

Java和Haskell有unboxed数组,但它们明显不如其他collection更方便。但是,如果需要最佳性能,避免装箱和取消装箱开销会带来一些不便。

*对于本次讨论,原始值是任何可以存储在the call stack上的值,而不是存储为指向堆上值的指针。通常这只是机器类型(整数,浮点数等),结构体,有时是静态大小的数组。 .NET-land将它们称为值类型(与引用类型相反)。 Java人称它们为原始类型。 Haskellions只是叫他们拆箱。

**我也专注于Java,Haskell和C#在这个答案,因为这就是我所知道的。对于它的价值,Python,Ruby和Javascript都具有专门的盒装值。这也被称为“一切都是对象”的方法***。注意:在某些情况下,足够先进的编译器/ JIT可以在运行时真正检测到在查看源代码时语义上装箱的值可以安全地为未装箱的值。从本质上讲,感谢辉煌的语言实现者,您的盒子有时是免费的。

  0

为什么尽管盒装价值,CLR或任何形式的拳击价值有什么好处? 26 10月. 102010-10-26 01:13:46

  0

总之(哈哈),它们只是另一个对象,它非常方便。基元(至少在Java中)不从Object中下降,不能有字段,不能有方法,并且通常表现与其他类型的值非常不同。另一方面,与他们合作可以非常快速和节省空间。因此折衷。 24 11月. 102010-11-24 19:22:57

+1

JavaScript有所谓的类型数组(新的UInt32Array等),它是非盒子和浮点数组。 30 6月. 122012-06-30 06:03:18


66

装箱&拆箱是将原始值转换为面向对象包装类(装箱),或将值从面向对象的包装类转换回原始值(拆箱)的过程。

例如,在Java中,你可以,如果你想将其存储在一个Collection因为原语不能被储存在Collection,只对象需要一个int值转换为Integer(拳)。但是,如果您想从Collection中取回数值,则可能需要将其值设为int而不是Integer,因此您可以将其取消。

拳击和拆箱并不是固有的不好,但它是一个折衷。根据语言的实现,它可能比只使用基元更慢,内存更密集。但是,它也可能允许您使用更高级别的数据结构,并在代码中实现更大的灵活性。

现在,它在Java(和其他语言)的“自动装箱/自动装箱”功能的上下文中经常被讨论。这是一个java centric explanation of autoboxing


23

在.NET中:

通常你不能依靠什么变量类型的功能会消耗,所以你需要使用从最小公分母延伸的对象变量 - 在.net这是object

但是object是一类,它的内容作为参考存储。

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 

虽然这两个都保持相同的信息,第二个列表更大更慢。第二个列表中的每个值实际上是对object的引用,其中包含int

这被称为盒装因为int是由object包装。当它退回int被解除封装 - 转换回它的价值。

对于值类型(即全部为structs),这很慢,并且可能会占用更多空间。

对于参考类型(即全部为classes),这远远不是一个问题,因为无论如何它们都存储为参考。

盒装值类型的另一个问题是,它不是显而易见的,而是处理盒子而不是值。当你比较两个structs时,你是在比较值,但是当你比较两个classes时,那么(默认情况下)你正在比较参考 - 即它们是否是同一个实例?

与装箱值类型时,这可能会造成混淆:

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 

很容易解决:

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 

不过那是另外的事情要小心装箱值打交道的。

+1

在vb.net中,相等语义之间的区别更加清楚,“Object”没有实现相等运算符,但类类型可以与“Is”运算符进行比较;相反,'Int32'可以与等号运算符一起使用,但不能是'Is'。这种区分使得比较更清晰。 07 12月. 122012-12-07 23:52:01


110

C# 3.0 In a Nutshell从:

拳击是铸造一个值 型成引用类型的行为:

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

开箱是...反向:

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

+1,现在**简洁**。 27 4月. 122012-04-27 11:27:33


3

在.NET FCL泛型集合:

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

都旨在克服拳击的性能问题,并在以前的集合实现拆箱。

欲了解更多信息,请参阅第16章CLR via C# (2nd Edition)


-2

像其他任何事情一样,如果不谨慎使用自动装箱,可能会产生问题。经典的结果是NullPointerException,无法追踪它。即使使用调试器。试试这个:

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

这只是不好的代码,并且与自动装箱无关。变量'i'被提前初始化。要么使之成为空声明('整数I;'),这样编译器可以指出的是,你忘了初始化,或等待宣布它,直到你知道它的价值。 24 9月. 082008-09-24 23:14:25

  0

嗯,如果我在try catch块内部做了一些事情,那么编译器会迫使我用一些东西来初始化它。 这是没有真正的代码 - 这是它如何可能发生的例子。 26 9月. 082008-09-26 14:24:24

  0

这显示了什么?绝对没有理由使用Integer对象。相反,你现在必须处理一个潜在的NullPointer。 29 6月. 102010-06-29 01:12:37


1

装箱是将值类型转换为引用类型的过程。

拆箱是将引用类型转换为值类型。

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

值类型是:
INT,炭和结构,枚举。 参考类型为: 类,接口,数组,字符串和对象


0

装箱和拆箱有助于将值类型视为对象。装箱意味着将一个值转换为对象引用类型的一个实例。例如,Int是一类,而int是数据类型。将int转换为Int是拳击的例证,而将Int转换为int将取消装箱。这个概念有助于垃圾收集,拆箱,另一方面,将对象类型转换为值类型。

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

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