ボクシングとアンボクシングとは何ですか?トレードオフは何ですか?


135

私は明確で簡潔で正確な答えを探しています。

良い説明へのリンクを歓迎しますが、理想的には実際の答えとして。


2
これは本当に言語に依存しませんか?
Henk Holterman、2012年

3
@HenkHoltermanは、言語固有ではありませんが、すべての言語に関連しているわけではありません。たとえば、ほとんどの動的型付き言語では、区別は関係ありません。代わりにどのタグを使用できるかわかりません- language-but-not-type-agnosticstatic-language-agnostic?SOが区別を必要とするかどうかはわかりません。メタには良い質問かもしれません。
キース

回答:


189

ボックス化された値は、プリミティブ型 *の最小ラッパーであるデータ構造です。ボックス化された値は通常、ヒープオブジェクトへポインタとして格納されます

したがって、ボックス化された値はより多くのメモリを使用し、アクセスするために最低2回のメモリルックアップを必要とします。明らかに、これは内側のループに必要な種類ではありません。一方、ボックス化された値は、通常、システム内の他のタイプとうまく機能します。これらは言語の第一級のデータ構造であるため、他のデータ構造と同様に期待されるメタデータと構造を持っています。

JavaおよびHaskellでは、ジェネリックコレクションにボックス化されていない値を含めることはできません。.NETのジェネリックコレクションは、ペナルティなしでボックス化されていない値を保持できます。Javaのジェネリックがコンパイル時の型チェックにのみ使用される場合、.NETは実行時にインスタンス化されるジェネリック型ごとに特定のクラスを生成します

JavaとHaskellにはボックス化されていない配列がありますが、他のコレクションよりも明らかに便利ではありません。ただし、ピーク時のパフォーマンスが必要な場合は、ボックス化とボックス化解除のオーバーヘッドを回避するのは少し不便です。

*この説明では、プリミティブ値は、ヒープ上の値へのポインターとして格納されるのではなく、呼び出しスタックに格納できる任意の値です。多くの場合、それはマシンタイプ(int、floatなど)、構造体、場合によっては静的サイズの配列だけです。.NETランドは、それらを(参照型ではなく)値型と呼びます。Javaの人々はそれらをプリミティブ型と呼びます。Haskellionsは、それらをunboxedと呼びます。

**この回答では、Java、Haskell、C#にも焦点を当てています。価値があるのは、Python、Ruby、Javascriptのすべてに、ボックス化された値だけが含まれていることです。これは「すべてがオブジェクトである」アプローチとしても知られています***。

***警告:十分に高度なコンパイラー/ JITは、ソースを見るときに意味的にボックス化された値を、実行時にボックス化解除された値として安全に検出できる場合があります。本質的に、見事な言語の実装者のおかげで、ボックスが無料になる場合があります。


ボックス化された値であるにもかかわらず、CLRやその他のものがボックス化された値を取得する利点は何ですか?
PositiveGuy

要するに(ハハ)、それらは今までにないほど便利な別のオブジェクトです。プリミティブ(少なくともJavaの場合)はObjectから派生したものではなく、フィールドを持つことはできず、メソッドを持つことはできません。一般に、他のタイプの値とは非常に異なる動作をします。一方、それらを使用すると、非常に高速でスペース効率がよくなります。したがって、トレードオフ。
Peter Burns

2
Javascriptには、ボックス化されていないintおよびfloatの配列である、いわゆる型付き配列(新しいUInt32Arrayなど)があります。
nponeccop 2012年

126

C#3.0から:

ボクシングとは、値型を参照型にキャストすることです。

int x = 9; 
object o = x; // boxing the int

unboxingは...逆です:

// unboxing o
object o = 9; 
int x = (int)o; 

72

ボクシングとアンボクシングは、プリミティブ値をオブジェクト指向ラッパークラスに変換する(ボクシング)、またはオブジェクト指向ラッパークラスの値をプリミティブ値に変換する(アンボクシング)プロセスです。

たとえば、Javaでは、プリミティブはオブジェクトにのみ格納できないため、int値をIntegerに格納する場合は、値を(ボクシング)に変換する必要があります。しかし、それを元に戻したい場合は、値をとしてではなくとして取得したい場合があります。CollectionCollectionCollectionintInteger

ボクシングとアンボクシングは本質的に悪いわけではありませんが、トレードオフです。言語の実装によっては、プリミティブを使用する場合よりも処理が遅くなり、メモリを大量に消費する可能性があります。ただし、より高いレベルのデータ構造を使用して、コードの柔軟性を高めることもできます。

最近では、Java(および他の言語)の「オートボクシング/自動アンボクシング」機能のコンテキストで最もよく議論されています。これはオートボクシングのJava中心の説明です


23

.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)の場合、参照として保存されるため、これは問題にはなりません。

ボックス化された値タイプのもう1つの問題は、値ではなくボックスを処理していることが明らかでないことです。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つ注意が必要です。


1
vb.netでは、等価セマンティクスの違いはより明確でObjectあり、等価演算子は実装されていませんが、クラス型はIs演算子と比較できます。逆に、Int32等号演算子と一緒に使用できますが、は使用できませんIs。この違いにより、どのタイプの比較が行われているかがより明確になります。
supercat

4

Boxing値タイプを参照タイプに変換するプロセスです。それに対してUnboxing、参照型から値型への変換です。

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

:値の型がありintcharそしてstructuresenumerations。参照型です: Classesinterfacesarraysstringsおよびobjects


3

.NET FCLジェネリックコレクション:

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

以前のコレクションの実装におけるボックス化とボックス化解除のパフォーマンスの問題を克服するために設計されました。

詳細については、Chapter 16、CLR via C#(2nd Edition)を参照してください。


1

ボックス化とボックス化解除により、値タイプをオブジェクトとして扱うことが容易になります。ボクシングとは、値をオブジェクト参照型のインスタンスに変換することを意味します。たとえば、Intはクラスであり、intはデータ型です。変換intするにInt変換する一方、ボクシングの例示であるIntとすると、intアンボクシングです。一方、このコンセプトはガベージコレクションに役立ちます。ボックス化解除は、オブジェクトタイプを値タイプに変換します。

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

o=123;
i=(int)o; //Unboxing.

JavaScriptでは、をvar ii = 123; typeof ii 返しますnumbervar iiObj = new Number(123); typeof iiObjを返しますobjecttypeof ii + iiObjを返しますnumber。したがって、これはボクシングと同等のJavaScriptです。値iiObjは、算術を実行してボックス化されていない値を返すために、プリミティブ番号(ボックス化されていない)に自動的に変換されました。
PatS

-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;)にして、初期化を忘れたことをコンパイラーが指摘できるようにするか、その値がわかるまで宣言を待機します。
エリクソン

うーん、try catchブロック内で何かを行うと、コンパイラーはそれを何かで初期化するように強制します。これは実際のコードではなく、それがどのように発生するかの例です。
PEELY 2008

これは何を示していますか?Integerオブジェクトを使用する理由はまったくありません。代わりに、潜在的なNullPointerに対処する必要があります。
Richard Clayton
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.