Что такое упаковка и распаковка и каковы компромиссы?
-
08-06-2019 - |
Вопрос
Я ищу ясный, краткий и точный ответ.
В идеале как фактический ответ, хотя ссылки на хорошие объяснения приветствуются.
Решение
Заключенные в коробку значения являются структуры данных это минимальные обертки вокруг примитивные типы*.Значения в штучной упаковке обычно хранятся в виде указателей на объекты на куча.
Таким образом, значения в штучной упаковке используют больше памяти и для доступа к ним требуется как минимум два поиска в памяти:один раз, чтобы получить указатель, и еще один, чтобы следовать за этим указателем на примитив.Очевидно, что это не та вещь, которую вы хотите использовать в своих внутренних циклах.С другой стороны, значения в штучной упаковке обычно лучше сочетаются с другими типами в системе.Поскольку они являются первоклассными структурами данных в языке, они обладают ожидаемыми метаданными и структурой, которые есть у других структур данных.
В Java и Haskell универсальные коллекции не могут содержать распакованных значений.Универсальные коллекции в .NET могут содержать распакованные значения без каких-либо штрафных санкций.Там, где дженерики Java используются только для проверки типов во время компиляции, .NET будет генерируйте конкретные классы для каждого универсального типа, создаваемого во время выполнения.
Java и Haskell имеют распакованные массивы, но они явно менее удобны, чем другие коллекции.Однако, когда требуется максимальная производительность, стоит прибегнуть к небольшим неудобствам, чтобы избежать накладных расходов на упаковку и распаковку.
* Для данного обсуждения примитивным значением является любое значение, которое может быть сохранено на стек вызовов, а не хранится как указатель на значение в куче.Часто это просто машинные типы (целые числа, значения с плавающей точкой и т.д.), Структуры и иногда массивы статического размера..NET-land называет их типами значений (в отличие от ссылочных типов).Разработчики Java называют их примитивными типами.Хаскеллионы просто называют их распакованными.
** В этом ответе я также фокусируюсь на Java, Haskell и C #, потому что это то, что я знаю.Как бы то ни было, Python, Ruby и Javascript имеют исключительно коробочные значения.Это также известно как подход "Все является объектом" ***.
*** Предостережение:Достаточно продвинутый компилятор / JIT может в некоторых случаях фактически обнаружить, что значение, которое семантически помещается в коробку при просмотре исходного кода, может безопасно быть распакованным значением во время выполнения.По сути, благодаря блестящим разработчикам языка ваши ящики иногда бесплатны.
Другие советы
Упаковка - это процесс преобразования значения типа в ссылочный тип:
int x = 9;
object o = x; // boxing the int
распаковка есть...обратное:
// unboxing o
object o = 9;
int x = (int)o;
Упаковка и распаковка - это процесс преобразования примитивного значения в объектно-ориентированный класс-оболочку (boxing) или преобразования значения из объектно-ориентированного класса-оболочки обратно в примитивное значение (unboxing).
Например, в java вам может потребоваться преобразовать int
значение в Integer
(бокс), если вы хотите сохранить его в Collection
потому что примитивы не могут храниться в Collection
, только объекты.Но когда вы захотите вытащить его обратно из Collection
возможно, вы захотите получить значение в виде int
и не является Integer
итак, вы бы распаковали его.
Упаковка и распаковка не являются неотъемлемой частью плохой, но это компромисс.В зависимости от языковой реализации это может быть медленнее и занимать больше памяти, чем простое использование примитивов.Однако это также может позволить вам использовать структуры данных более высокого уровня и добиться большей гибкости в вашем коде.
В наши дни это чаще всего обсуждается в контексте функции "автоматической отправки / autounboxing" в Java (и других языках).Вот такой java-ориентированное объяснение автоматической упаковки.
В .Net:
Часто вы не можете полагаться на тип переменной, которую будет использовать функция, поэтому вам нужно использовать объектную переменную, которая простирается от наименьшего общего знаменателя - in .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
Однако это еще одна вещь, с которой следует быть осторожным, когда имеешь дело со значениями в штучной упаковке.
Общие коллекции .NET FCL:
List<T>
Dictionary<TKey, UValue>
SortedDictionary<TKey, UValue>
Stack<T>
Queue<T>
LinkedList<T>
все они были разработаны для решения проблем с производительностью упаковки и распаковки в предыдущих реализациях collection.
Дополнительные сведения см. в главе 16, Среда CLR через C# (2-е издание).
Упаковка - это процесс преобразования типа значения в ссылочный тип.
Распаковка - это преобразование ссылочного типа в тип значения.
EX: int i=123;
object o=i;// Boxing
int j=(int)o;// UnBoxing
Типом значения являются:
int, char и структуры, перечисления.Ссылочным типом являются:Классы, интерфейсы, массивы, строки и объекты
Упаковка и распаковка облегчает обработку типов значений как объектов.Упаковка означает преобразование значения в экземпляр ссылочного типа объекта.Например, Int
является классом и int
это тип данных.Преобразование int
Для Int
является примером бокса, в то время как преобразование Int
Для int
идет распаковка коробки.Концепция помогает при сборке мусора, распаковке, с другой стороны, преобразует тип объекта в тип значения.
int i=123;
object o=(object)i; //Boxing
o=123;
i=(int)o; //Unboxing.
Как и все остальное, автоматическая упаковка может быть проблематичной, если ее использовать неосторожно.Классический вариант - получить исключение 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;
}
}