Что такое бокс и распаковка и каковы компромиссы? - PullRequest
125 голосов
/ 16 августа 2008

Я ищу ясный, краткий и точный ответ.

В идеале, как фактический ответ, хотя ссылки на хорошие объяснения приветствуются.

Ответы [ 8 ]

180 голосов
/ 25 августа 2008

Значения в штучной упаковке: структуры данных , которые являются минимальными оболочками для примитивных типов *. Штучные значения обычно хранятся в виде указателей на объекты в куче .

Таким образом, значения в штучной упаковке занимают больше памяти и требуют как минимум два поиска в памяти: один - для получения указателя, а другой - для следования этому указателю на примитив. Очевидно, это не та вещь, которую вы хотите в своих внутренних циклах. С другой стороны, значения в штучной упаковке обычно лучше работают с другими типами в системе. Поскольку они являются первоклассными структурами данных в языке, они имеют ожидаемые метаданные и структуру, которые имеют другие структуры данных.

В Java и Haskell универсальные коллекции не могут содержать распакованных значений. Родовые коллекции в .NET могут содержать распакованные значения без штрафов. Там, где универсальные шаблоны Java используются только для проверки типов во время компиляции, .NET будет генерировать конкретные классы для каждого универсального типа, создаваемого во время выполнения .

Java и Haskell имеют распакованные массивы, но они явно менее удобны, чем другие коллекции. Тем не менее, когда требуется максимальная производительность, стоит немного неудобств, чтобы избежать накладных расходов на упаковку и распаковку.

* Для этого обсуждения примитивное значение - это любое значение, которое может храниться в стеке вызовов , а не храниться в виде указателя на значение в куче. Часто это просто типы машин (целые, плавающие и т. Д.), Структуры, а иногда и массивы статических размеров. .NET-land называет их типами значений (в отличие от ссылочных типов). Java-люди называют их примитивными типами. Хаскеллионы просто называют их без коробки.

** В этом ответе я также фокусируюсь на Java, Haskell и C #, потому что это то, что я знаю. Для чего бы это ни стоило, Python, Ruby и Javascript имеют исключительно упакованные значения. Это также известно как подход «Все является объектом» ***.

*** Предупреждение: достаточно продвинутый компилятор / JIT может в некоторых случаях фактически обнаружить, что значение, которое семантически заключено в коробку при просмотре источника, может безопасно быть распакованным значением во время выполнения. По сути, благодаря блестящим языковым разработчикам ваши ящики иногда свободны.

122 голосов
/ 16 августа 2008

из C # 3.0 В двух словах :

Бокс - это акт приведения значения введите ссылочный тип:

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

распаковка это ... обратное:

// unboxing o
object o = 9; 
int x = (int)o; 
68 голосов
/ 16 августа 2008

Упаковка и распаковка - это процесс преобразования примитивного значения в объектно-ориентированный класс-оболочку (бокс) или преобразования значения из объектно-ориентированного класса-оболочки обратно в примитивное значение (распаковка).

Например, в java вам может потребоваться преобразовать значение int в Integer (бокс), если вы хотите сохранить его в Collection, потому что примитивы не могут быть сохранены в Collection , только предметы. Но когда вы хотите вернуть его из Collection, вы можете получить значение как int, а не Integer, так что вы бы его распаковали.

Бокс и распаковка по своей сути не плохо , но это компромисс. В зависимости от языковой реализации, он может быть медленнее и занимать больше памяти, чем просто использование примитивов. Однако это также может позволить вам использовать структуры данных более высокого уровня и добиться большей гибкости в вашем коде.

В наши дни, это чаще всего обсуждается в контексте функции «autoboxing / autounboxing» Java (и других языков). Вот Java-ориентированное объяснение автобокса .

23 голосов
/ 16 августа 2008

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

Однако при работе со значениями в штучной упаковке следует соблюдать осторожность.

3 голосов
/ 16 августа 2008

Общие коллекции .NET FCL:

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

все были разработаны для преодоления проблем с производительностью упаковки и распаковки в предыдущих реализациях коллекции.

Подробнее см. Главу 16, CLR через C # (2-е издание) .

2 голосов
/ 06 февраля 2014

Бокс - это процесс преобразования типа значения в ссылочный тип.

Распаковка - это преобразование ссылочного типа в тип значения.

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

Тип значения:
int, char и структуры, перечисления. Тип ссылки: Классы, интерфейсы, массивы, строки и объекты

1 голос
/ 21 июня 2016

Упаковка и распаковка облегчает восприятие типов значений как объектов. Бокс означает преобразование значения в экземпляр ссылочного типа объекта. Например, Int - это класс, а int - это тип данных. Преобразование int в Int является примером бокса, тогда как преобразование Int в int является распаковкой Эта концепция помогает в сборке мусора. С другой стороны, распаковка преобразует тип объекта в тип значения.

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

o=123;
i=(int)o; //Unboxing.
0 голосов
/ 19 сентября 2008

Как и все остальное, автобокс может быть проблематичным, если не использовать его осторожно. Классика заключается в том, чтобы получить исключение 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;
    }
}
...