ボックス化とアンボックス化とは何ですか?またそのトレードオフは何ですか?
-
08-06-2019 - |
質問
明確、簡潔、正確な答えを探しています。
理想的には実際の答えですが、適切な説明へのリンクは歓迎します。
解決
ボックス内の値は次のとおりです。 データ構造 それは最小限のラッパーです プリミティブ型*。ボックス化された値は通常、オブジェクトへのポインタとして保存されます。 ヒープ.
したがって、ボックス化された値はより多くのメモリを使用し、アクセスするには少なくとも 2 回のメモリ検索が必要になります。1 回目はポインターを取得するため、もう 1 回目はプリミティブへのポインターを追跡します。明らかに、これは内部ループで望むようなものではありません。一方、ボックス化された値は、通常、システム内の他の型とより適切に連携します。これらは言語のファーストクラスのデータ構造であるため、他のデータ構造が持つ予期されるメタデータと構造を備えています。
Java と Haskell では、ジェネリック コレクションにボックス化されていない値を含めることはできません。.NET の汎用コレクションは、ボックス化されていない値をペナルティなしで保持できます。Java のジェネリックスはコンパイル時の型チェックにのみ使用されますが、.NET では 実行時にインスタンス化されるジェネリック型ごとに特定のクラスを生成する.
Java と Haskell にはボックス化されていない配列がありますが、他のコレクションに比べて明らかに利便性が劣ります。ただし、最高のパフォーマンスが必要な場合は、ボックス化とボックス化解除のオーバーヘッドを回避するために多少の不便を伴う価値があります。
* この説明では、プリミティブ値とは、保存できる値のことです。 コールスタック, ヒープ上の値へのポインタとして保存されるのではなく、多くの場合、それはマシンタイプ (int、float など)、構造体、そして場合によっては静的なサイズの配列だけです。.NET-land では、これらを (参照型ではなく) 値型と呼びます。Java の人々はこれらをプリミティブ型と呼びます。ハスケリオンは単にボックス化されていないものと呼びます。
** この回答では Java、Haskell、C# にも焦点を当てています。それは私が知っていることだからです。当然のことながら、Python、Ruby、JavaScript はすべてボックス化された値のみを持っています。これは、「すべてがオブジェクトである」アプローチ***としても知られています。
*** 警告:十分に高度なコンパイラ/JIT は、場合によっては、ソースを見たときに意味的にボックス化されている値が、実行時には安全にボックス化されていない値である可能性があることを実際に検出できます。本質的に、優れた言語実装者のおかげで、ボックスが無料になる場合があります。
他のヒント
から C# 3.0 の概要:
ボクシングは、値タイプを参照タイプにキャストする行為です。
int x = 9;
object o = x; // boxing the int
開封というのは…逆:
// unboxing o
object o = 9;
int x = (int)o;
ボックス化とアンボックス化は、プリミティブ値をオブジェクト指向ラッパー クラスに変換する (ボックス化)、またはオブジェクト指向ラッパー クラスの値をプリミティブ値に変換する (アンボックス化) プロセスです。
たとえば、Java では、次のように変換する必要がある場合があります。 int
値を Integer
(箱詰め) に保管したい場合は、 Collection
プリミティブは に保存できないため、 Collection
, 、オブジェクトのみ。しかし、それを元に戻したいときは、 Collection
として値を取得したい場合があります int
ではありません Integer
それで箱を開けます。
ボックス化とボックス化解除は本質的にはありません 悪い, 、しかしそれはトレードオフです。言語の実装によっては、単にプリミティブを使用する場合よりも遅くなり、メモリの消費量が多くなる場合があります。ただし、より高いレベルのデータ構造を使用して、コードの柔軟性を高めることもできる場合があります。
最近では、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
これらはどちらも同じ情報を保持しますが、2 番目のリストの方が大きく、処理が遅くなります。2 番目のリストの各値は、実際には、 object
それは int
.
これはボックス化と呼ばれます。 int
で包まれています object
. 。キャストバックすると、 int
ボックス化されていない - 値に変換されて戻されます。
値の型の場合 (つまり、全て structs
)これは遅く、より多くのスペースを使用する可能性があります。
参照型の場合 (つまり、全て classes
)とにかく参照として保存されるため、これはそれほど問題ではありません。
ボックス化された値型に関するさらに問題は、値ではなくボックスを扱っていることが明らかではないことです。二つを比べてみると structs
値を比較しているのですが、2 つを比較すると、 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>
これらはすべて、以前のコレクション実装におけるボックス化とボックス化解除のパフォーマンスの問題を克服するように設計されています。
詳細については、第 16 章を参照してください。 C# 経由の CLR (第 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;
}
}