明確で簡潔で正確な答えを探しています。
良い説明へのリンクを歓迎しますが、理想的には実際の答えとして。
ボックスで囲まれた値は、プリミティブ型の最小限のラッパーであるデータ構造です*。ボックス化された値は通常、ヒープ上のオブジェクトへのポインターとして格納されます。
したがって、ボックス化された値はより多くのメモリを使用し、アクセスするために少なくとも 2 つのメモリ ルックアップが必要です。明らかに、これは内部ループで必要な種類のものではありません。一方、ボックス化された値は通常、システム内の他のタイプとうまく機能します。それらは言語の第一級のデータ構造であるため、他のデータ構造が持つ期待されるメタデータと構造を持っています。
Java および Haskell では、ジェネリック コレクションにボックス化されていない値を含めることはできません。.NET のジェネリック コレクションは、ボックス化されていない値をペナルティなしで保持できます。Java のジェネリックはコンパイル時の型チェックにのみ使用されますが、.NET は実行時にインスタンス化された各ジェネリック型に対して特定のクラスを生成します。
Java と Haskell にはボックス化されていない配列がありますが、他のコレクションよりも明らかに便利ではありません。ただし、最高のパフォーマンスが必要な場合は、ボックス化とボックス化解除のオーバーヘッドを回避するのが少し面倒です。
* この説明では、プリミティブ値は、ヒープ上の値へのポインターとして格納されるのではなく、コール スタックに格納できる任意の値です。多くの場合、それは単なるマシン タイプ (int、float など)、構造体、および場合によっては静的サイズの配列です。.NET ランドでは、それらを値型と呼びます (参照型ではなく)。Java の人々はそれらをプリミティブ型と呼んでいます。Haskellions は単にボックス化されていないと呼びます。
** この回答では、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;
Boxing&unboxingは、プリミティブ値をオブジェクト指向ラッパークラスに変換するプロセス(ボクシング)、またはオブジェクト指向ラッパークラスからプリミティブ値に値を変換するプロセス(アンボックス化)です。
たとえば、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
)の場合、とにかく参照として保存されるため、これはそれほど問題にはなりません。
ボックス化された値型のさらなる問題は、値ではなく、ボックスを扱っていることが明らかでないことです。2つを比較するとき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
ただし、ボックス化された値を処理するときに注意する必要があるもう1つのことです。
.NET FCLジェネリックコレクション:
List<T>
Dictionary<TKey, UValue>
SortedDictionary<TKey, UValue>
Stack<T>
Queue<T>
LinkedList<T>
これらはすべて、以前のコレクション実装でのボクシングとアンボクシングのパフォーマンスの問題を克服するように設計されていました。
詳細については、第16章「C#によるCLR(第2版) 」を参照してください。
ボックス化とボックス化解除により、値の型をオブジェクトとして扱うことが容易になります。ボクシングとは、値をオブジェクト参照型のインスタンスに変換することです。たとえば、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;
}
}