首页 文章

什么是拳击和拆箱以及有什么权衡取舍?

提问于
浏览
124

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

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

8 回答

  • 179

    盒装值是data structures,它们是primitive types *周围的最小包装器 . 盒装值通常存储为the heap上对象的指针 .

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

    在Java和Haskell中,泛型集合只能用于编译时类型检查,.NET将generate specific classes for each generic type instantiated at run time .

    Java和Haskell有未装箱的数组,但它们明显不如其他集合方便 . 但是,当需要达到峰值性能时,为避免装箱和拆箱的开销值得一点不便 .

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

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

    ***警告:在某些情况下,足够先进的编译器/ JIT实际上可以检测到在查看源时语义上装箱的值,可以在运行时安全地成为未装箱的值 . 实质上,由于出色的语言实现者,您的盒子有时是免费的 .

  • 3

    来自C# 3.0 In a Nutshell

    Boxing是将值类型转换为引用类型的行为:

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

    拆箱是......相反的:

    // unboxing o
    object o = 9; 
    int x = (int)o;
    
  • -2

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

    例如,在java中,如果要将 int 值存储在_285980中,则可能需要将其转换为 Integer (装箱),因为基元不能存储在 Collection 中,只能存储在对象中 . 但是当你想要从 Collection 中取回它时,你可能希望得到一个 int 而不是 Integer 的值,所以你要取消它 .

    拳击和拆箱本身并不坏,但这是一个权衡 . 根据语言实现,它可能比仅使用基元更慢且占用更多内存 . 但是,它还可以允许您使用更高级别的数据结构,并在代码中实现更大的灵活性 .

    目前,它最常见于Java 's (and other language' s)"autoboxing/autounboxing"功能的上下文中 . 这是java centric explanation of autoboxing .

  • 1

    在.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
    

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

    这被称为盒装,因为 intobject 包裹 . 当它退回时, 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
    

    不过它在处理盒装值时要小心另一件事 .

  • 2

    .NET FCL通用集合:

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

    所有这些都旨在克服先前集合实现中装箱和拆箱的性能问题 .

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

  • 23

    Boxing是将值类型转换为引用类型的过程 .

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

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

    Value 类型是:
    int,char和结构,枚举 . 引用类型包括:类,接口,数组,字符串和对象

  • 68

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

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

    与其他任何事情一样,如果不仔细使用,自动装箱可能会出现问题 . 经典是以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;
        }
    }
    

相关问题