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

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

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

盒装值是基本types *周围最小包装的数据结构 。 盒装值通常被存储为指向堆上对象的指针。

因此,boxed值使用更多的内存,并至less要访问两个内存查找:一次获取指针,另一个访问该指针原始指针。 显然这不是你内心循环中想要的那种东西。 另一方面,盒装值在系统中的其他types通常会更好。 由于它们是语言中的一stream数据结构,因此它们具有其他数据结构所具有的预期元数据和结构。

在Java和Haskell中,通用集合不能包含取消装箱的值。 .NET中的通用集合可以保留无装箱值,不会受到任何处罚。 在Java的generics只用于编译时types检查的情况下,.NET将为运行时实例化的每个genericstypes生成特定的类 。

Java和Haskell有unboxed数组,但它们明显不如其他集合方便。 然而,当需要高峰performance时,避免拳击和拆箱的开销会带来一些不便。

*对于这个讨论,原始值是可以存储在调用堆栈中的任何值,而不是作为指向堆中值的指针存储。 经常这只是机器types(整数,浮点数等),结构体,有时是静态大小的数组。 .NET-land调用它们的值types(与引用types相反)。 爪哇人称他们为原始types。 Haskellions只是叫他们拆箱。

**在这个答案中,我也专注于Java,Haskell和C#,因为这就是我所知道的。 对于什么是值得的,Python,Ruby和Javascript都有专门的盒装值。 这也被称为“一切都是对象”的方法***。

***注意:在某些情况下,足够先进的编译器/ JIT可以在运行时真正检测到在查看源代码时在语义上装箱的值,可以安全地作为unboxed值。 从本质上讲,多亏了辉煌的语言实现者,你的盒子有时是免费的。

来自C#3.0简而言之 :

拳击是将值types转换为引用types的行为:

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

拆箱是…相反:

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

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

例如,在java中,如果要将它存储到Collection ,则可能需要将int值转换为Integer (装箱),因为基元不能存储在Collection ,只能是对象。 但是当你想把它从Collection取出时,你可能想要把值作为一个int而不是一个Integer所以你可以解开它。

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

目前,Java(和其他语言)的“自动装箱/自动装箱”function是最常讨论的。 这是一个以Java为中心的自动装箱解释 。

在.Net中:

通常你不能依赖函数会消耗什么types的variables,所以你需要使用一个扩展自最小公分母的对象variables – 在.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 

虽然这两个都持有相同的信息,第二个名单是更大,更慢。 第二个列表中的每个值实际上是对保存intobject的引用。

这被称为盒装因为int是由object包装。 当它返回时, int被解除封装 – 转换回它的值。

对于值types(即所有structs ),这是缓慢的,并可能使用更多的空间。

对于引用types(即所有classes ),这是一个问题,因为它们无论如何都被存储为参考。

盒装值types的另一个问题是不清楚你是在处理盒子,而不是价值。 当你比较两个structs然后你比较值,但是当你比较两个classes然后(默认情况下)你比较参考 – 即这些是同一个实例吗?

处理盒装值types时可能会造成混淆:

 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 

然而,在处理盒装值时要小心另一件事。

.NET FCL通用集合:

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

都是为了克服之前集合实现中的装箱和拆箱的性能问题而devise的。

有关更多信息,请参见第16章, CLR via C#(2nd Edition)

拳击是将值types转换为引用types的过程。

拆箱是将引用types转换为值types。

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

值types是:
int,char和结构,枚举。 引用types是:类,接口,数组,string和对象

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

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

像其他任何东西一样,如果不仔细使用,自动装箱可能会有问题。 经典的结果是一个NullPointerException,不能跟踪它。 即使有一个debugging器。 尝试这个:

 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; } } 
Interesting Posts