我正在寻找一个清晰,简洁和准确的答案。
理想情况下,作为实际答案,尽管欢迎链接到好的解释。
答案 0 :(得分:180)
盒装值为data structures,它们是primitive types *周围的最小包装器。盒装值通常存储为the heap上对象的指针。
因此,盒装值使用更多内存并至少需要两次内存查找才能访问:一次获取指针,另一次跟随指向该基元的指针。显然,这不是你想要在内循环中的那种东西。另一方面,盒装值通常与系统中的其他类型相比更好。由于它们是该语言中的一流数据结构,因此它们具有其他数据结构所具有的预期元数据和结构。
在Java和Haskell中,泛型集合不能包含未装箱的值。 .NET中的通用集合可以保留未装箱的值而不会受到惩罚。 Java的泛型仅用于编译时类型检查,.NET将generate specific classes for each generic type instantiated at run time。
Java和Haskell有未装箱的数组,但它们明显不如其他集合方便。但是,当需要达到峰值性能时,为避免装箱和拆箱的开销值得一点不便。
*对于此讨论,原始值是可以存储在the call stack上的任何值,而不是存储为指向堆上的值的指针。通常,这只是机器类型(整数,浮点数等),结构,有时是静态大小的数组。 .NET-land将它们称为值类型(与引用类型相反)。 Java人称它们为原始类型。 Haskellions只是将它们称为未装箱。
**我也在这个答案中专注于Java,Haskell和C#,因为这就是我所知道的。对于它的价值,Python,Ruby和Javascript都只有盒装价值。这也被称为“一切都是对象”的方法***。
***警告:在某些情况下,足够先进的编译器/ JIT实际上可以检测到在查看源时语义上装箱的值,可以在运行时安全地成为未装箱的值。从本质上讲,由于出色的语言实现者,你的盒子有时是免费的。
答案 1 :(得分:122)
拳击是铸造价值的行为 输入引用类型:
int x = 9;
object o = x; // boxing the int
拆箱是......相反的:
// unboxing o
object o = 9;
int x = (int)o;
答案 2 :(得分:68)
拳击&拆箱是将原始值转换为面向对象的包装类(装箱),或将面向对象的包装类中的值转换回原始值(拆箱)的过程。
例如,在java中,如果要将int
存储在Integer
中,则可能需要将Collection
值转换为Collection
(拳击),因为基元不能存储在Collection
中的唯一对象。但是,如果您希望将其从int
中取回,则可能希望将值设为Integer
而不是{{1}},以便将其取消装箱。
拳击和拆箱本身并不是坏,但这是一种权衡。根据语言实现,它可能比仅使用基元更慢且占用更多内存。但是,它也可能允许您使用更高级别的数据结构,并在代码中实现更大的灵活性。
现在,最常见的是在Java(和其他语言)“autoboxing / autounboxing”功能的上下文中讨论。这是java centric explanation of autoboxing。
答案 3 :(得分: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
的{{1}}的引用。
这称为盒装,因为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
然而,在处理盒装值时要小心另一件事。
答案 4 :(得分:3)
.NET FCL泛型集合:
List<T>
Dictionary<TKey, UValue>
SortedDictionary<TKey, UValue>
Stack<T>
Queue<T>
LinkedList<T>
旨在克服先前集合实现中装箱和拆箱的性能问题。
有关详情,请参阅第16章CLR via C# (2nd Edition)。
答案 5 :(得分:2)
拳击是将值类型转换为引用类型的过程。
取消装箱是将参考类型转换为值类型。
EX: int i=123;
object o=i;// Boxing
int j=(int)o;// UnBoxing
价值类型是:
int,char和结构,枚举。
参考类型是:
类,接口,数组,字符串和对象
答案 6 :(得分:1)
装箱和拆箱有助于将值类型视为对象。拳击意味着将值转换为对象引用类型的实例。例如,Int
是一个类,int
是一种数据类型。将int
转换为Int
是拳击的示例,而将Int
转换为int
则是取消装箱。这个概念有助于垃圾收集,另一方面,拆箱,将对象类型转换为值类型。
int i=123;
object o=(object)i; //Boxing
o=123;
i=(int)o; //Unboxing.
答案 7 :(得分:-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;
}
}