Я ищу четкий, краткий и точный ответ.
В идеале как фактический ответ, хотя ссылки на хорошие объяснения приветствуются.
Значения в штучной упаковке - это структуры данных, которые являются минимальными оболочками вокруг примитивных типов *. Штучные значения обычно хранятся в виде указателей на объекты в куче .
Таким образом, упакованные значения используют больше памяти и требуют как минимум два поиска в памяти: один - для получения указателя, а другой - для того, чтобы следовать этому указателю на примитив. Очевидно, что это не та вещь, которую вы хотите в своих внутренних циклах. С другой стороны, значения в штучной упаковке обычно лучше работают с другими типами в системе. Так как они являются первоклассными структурами данных в языке, они имеют ожидаемые метаданные и структуру, которые имеют другие структуры данных.
В Java и Haskell универсальные коллекции не могут содержать распакованных значений. Родовые коллекции в .NET могут содержать распакованные значения без штрафов. В тех случаях, когда дженерики Java используются только для проверки типов во время компиляции, .NET генерирует определенные классы для каждого родового типа, создаваемого во время выполнения .
Java и Haskell имеют распакованные массивы, но они явно менее удобны, чем другие коллекции. Однако, когда требуется максимальная производительность, стоит немного неудобств, чтобы избежать накладных расходов на упаковку и распаковку.
* Для этого обсуждения примитивное значение - это любое, которое может быть сохранено в стеке вызовов , а не сохранено как указатель на значение в куче. Часто это просто типы машин (целые, плавающие и т. Д.), Структуры, а иногда и статические массивы. .NET-land называет их типами значений (в отличие от ссылочных типов). Java-люди называют их примитивными типами. Хаскеллионы просто называют их без коробки.
** В этом ответе я также фокусируюсь на Java, Haskell и C #, потому что это то, что я знаю. Для чего бы это ни стоило, Python, Ruby и Javascript имеют исключительно упакованные значения. Это также известно как подход «Все является объектом» ***.
*** Предостережение: достаточно продвинутый компилятор / JIT может в некоторых случаях фактически обнаружить, что значение, которое семантически упаковано при взгляде на источник, может безопасно быть распакованным значением во время выполнения. По сути, благодаря блестящим языковым разработчикам ваши ящики иногда свободны.
Упаковка и распаковка - это процесс преобразования примитивного значения в объектно-ориентированный класс-оболочку (бокс) или преобразование значения из объектно-ориентированного класса-оболочки обратно в примитивное значение (распаковка).
Например, в Java вам может понадобиться преобразовать int
значение в Integer
(бокс), если вы хотите сохранить его в, Collection
потому что примитивы не могут быть сохранены Collection
только в объектах. Но когда вы хотите получить его обратно, Collection
вы можете получить значение как, int
а не как, Integer
чтобы распаковать его.
Бокс и распаковка - это не плохо , но это компромисс. В зависимости от языковой реализации, он может быть медленнее и занимать больше памяти, чем просто использование примитивов. Однако это также может позволить вам использовать структуры данных более высокого уровня и добиться большей гибкости в вашем коде.
В наши дни, это чаще всего обсуждается в контексте функции «autoboxing / autounboxing» Java (и других языков). Вот Java-ориентированное объяснение автобокса .
В .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
Однако при работе с коробочными значениями следует соблюдать осторожность.
Object
не реализует оператор равенства, но типы классов можно сравнивать с Is
оператором; и наоборот, Int32
может использоваться с оператором равенства, но не Is
. Это различие делает намного более ясным, какой тип сравнения делается. Бокс - это акт приведения типа значения в ссылочный тип:
int x = 9;
object o = x; // boxing the int
распаковка это ... обратное
// unboxing o
object o = 9;
int x = (int)o;
Общие коллекции .NET FCL:
List<T>
Dictionary<TKey, UValue>
SortedDictionary<TKey, UValue>
Stack<T>
Queue<T>
LinkedList<T>
все они были разработаны для преодоления проблем с производительностью упаковки и распаковки в предыдущих реализациях коллекции.
Для получения дополнительной информации см. Главу 16, CLR через C # (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;
}
}
i
инициализируется преждевременно. Либо сделайте его пустым объявлением ( Integer i;
), чтобы компилятор мог указать, что вы забыли его инициализировать, либо дождитесь его объявления, пока не узнаете его значение. Boxing
это процесс преобразования типа значения в ссылочный тип. Принимая во внимание Unboxing
, что преобразование ссылочного типа в тип значения.
EX: int i = 123;
object o = i;// Boxing
int j = (int)o;// UnBoxing
Типы значений являются: int
, char
и structures
, enumerations
. Ссылочные типы являются:
Classes
, interfaces
, arrays
, strings
иobjects
Упаковка и распаковка облегчает восприятие типов значений как объектов. Бокс означает преобразование значения в экземпляр ссылочного типа объекта. Например, Int
является классом и int
типом данных. Преобразование int
в Int
является примером бокса, а преобразование Int
в int
распаковку. Эта концепция помогает в сборке мусора. С другой стороны, распаковка преобразует тип объекта в тип значения.
int i=123;
object o=(object)i; //Boxing
o=123;
i=(int)o; //Unboxing.
var ii = 123; typeof ii
возвращается number
. var iiObj = new Number(123); typeof iiObj
возвращается object
. typeof ii + iiObj
возвращается number
. Так что это javascript-эквивалент бокса. Значение iiObj было автоматически преобразовано в примитивное число (без коробки), чтобы выполнить арифметику и вернуть значение без коробки.
language-but-not-type-agnostic
?static-language-agnostic
? Я не уверен, что ТАК нужно различие; может быть, хороший вопрос для мета, хотя.