Что такое бокс и распаковка и каковы компромиссы?

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

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


person Keith    schedule 16.08.2008    source источник
comment
Это действительно язык-агностик?   -  person Henk Holterman    schedule 29.10.2012
comment
@HenkHolterman это определенно не зависит от языка, хотя это также не относится к всем языкам - например, различие не будет иметь значения для большинства языков с динамической типизацией. Я не уверен, какой тег можно использовать вместо этого - language-but-not-type-agnostic? static-language-agnostic? Я не уверен, что SO нужно различие; может быть хорошим вопросом для меты.   -  person Keith    schedule 29.10.2012


Ответы (8)


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

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

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

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

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

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

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

person Peter Burns    schedule 24.08.2008
comment
Почему, хотя значение в штучной упаковке, какая выгода от CLR или чего-то еще, получает значения упаковки из формы? - person PositiveGuy; 26.10.2010
comment
Короче (ха-ха), это просто еще один объект, что очень удобно. Примитивы (по крайней мере, в Java) не происходят от Object, не могут иметь полей, не могут иметь методов и просто обычно ведут себя очень иначе, чем другие типы значений. С другой стороны, работа с ними может быть очень быстрой и экономичной. Таким образом, компромисс. - person Peter Burns; 24.11.2010
comment
В Javascript есть так называемые типизированные массивы (новый UInt32Array и т. Д.), Которые представляют собой массивы распакованных целых чисел и чисел с плавающей запятой. - person nponeccop; 30.06.2012


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

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

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

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

person Justin Standard    schedule 16.08.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

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

person Keith    schedule 16.08.2008
comment
В vb.net различие между семантикой равенства более четкое, Object не реализует оператор равенства, но типы классов можно сравнивать с оператором Is; и наоборот, Int32 можно использовать с оператором равенства, но не Is. Это различие проясняет, какой тип сравнения проводится. - person supercat; 08.12.2012

Boxing - это процесс преобразования типа значения в ссылочный тип. В то время как Unboxing - это преобразование ссылочного типа в тип значения.

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

Типы значений: int, char и structures, enumerations. Типы ссылок: _8 _, _ 9 _, _ 10 _, _ 11_ и objects

person vani    schedule 05.02.2014

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

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

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

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

person Jonathan Webb    schedule 16.08.2008

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

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

o=123;
i=(int)o; //Unboxing.
person Sanjay Kumar    schedule 21.06.2016
comment
В javascript var ii = 123; typeof ii возвращает number. var iiObj = new Number(123); typeof iiObj возвращает object. typeof ii + iiObj возвращает number. Так что это эквивалент бокса в javascript. Значение iiObj было автоматически преобразовано в примитивное число (распакованное), чтобы выполнить арифметику и вернуть распакованное значение. - person PatS; 19.11.2018

Как и все остальное, автобоксинг может быть проблематичным, если его не использовать осторожно. Классический вариант - получить исключение 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;
    }
}
person PEELY    schedule 19.09.2008
comment
Это просто плохой код и не имеет ничего общего с автобоксингом. Переменная i инициализирована преждевременно. Либо сделайте его пустым объявлением (Integer i;), чтобы компилятор мог указать, что вы забыли его инициализировать, либо подождите, чтобы объявить его, пока вы не узнаете его значение. - person erickson; 25.09.2008
comment
Хм, и если я сделаю что-то промежуточное внутри блока try catch, компилятор заставит меня инициализировать его чем-нибудь. Это не настоящий код - это пример того, как это могло произойти. - person PEELY; 26.09.2008
comment
Что это демонстрирует? Нет абсолютно никаких причин использовать объект Integer. Вместо этого вам теперь нужно иметь дело с потенциальным NullPointer. - person Richard Clayton; 29.06.2010