Was ist Boxen und Unboxen und was sind die Kompromisse?


135

Ich suche eine klare, präzise und genaue Antwort.

Idealerweise als eigentliche Antwort, obwohl Links zu guten Erklärungen willkommen sind.


2
Ist das wirklich sprachunabhängig?
Henk Holterman

3
@HenkHolterman es ist sicherlich nicht sprachspezifisch, obwohl es auch nicht für alle Sprachen relevant ist - die Unterscheidung ist beispielsweise für die meisten dynamisch typisierten Sprachen irrelevant. Ich bin nicht sicher, welches Tag stattdessen verwendet werden könnte - language-but-not-type-agnostic? static-language-agnostic? Ich bin nicht sicher, ob SO die Unterscheidung braucht; könnte aber eine gute Frage für Meta sein.
Keith

Antworten:


189

Boxed-Werte sind Datenstrukturen, bei denen es sich um minimale Wrapper um primitive Typen * handelt. Boxed-Werte werden normalerweise als Zeiger auf Objekte auf dem Heap gespeichert .

Daher verwenden Box-Werte mehr Speicher und benötigen mindestens zwei Speichersuchen, um darauf zuzugreifen: einmal, um den Zeiger abzurufen, und einmal, um diesem Zeiger auf das Grundelement zu folgen. Offensichtlich ist dies nicht die Art von Dingen, die Sie in Ihren inneren Schleifen wollen. Auf der anderen Seite spielen Box-Werte normalerweise besser mit anderen Typen im System. Da es sich um erstklassige Datenstrukturen in der Sprache handelt, verfügen sie über die erwarteten Metadaten und Strukturen anderer Datenstrukturen.

In Java und Haskell dürfen generische Sammlungen keine Werte ohne Box enthalten. Generische Sammlungen in .NET können Werte ohne Box ohne Strafen enthalten. Wenn Javas Generika nur zur Überprüfung des Typs zur Kompilierungszeit verwendet werden, generiert .NET für jeden zur Laufzeit instanziierten generischen Typ bestimmte Klassen .

Java und Haskell haben Arrays ohne Box, aber sie sind deutlich weniger praktisch als die anderen Sammlungen. Wenn jedoch Spitzenleistung benötigt wird, ist es ein wenig unangenehm, den Aufwand für das Boxen und Entpacken zu vermeiden.

* In dieser Diskussion ist ein primitiver Wert ein beliebiger Wert, der auf dem Aufrufstapel gespeichert werden kann , anstatt als Zeiger auf einen Wert auf dem Heap gespeichert zu werden. Häufig sind dies nur die Maschinentypen (Ints, Floats usw.), Strukturen und manchmal Arrays mit statischer Größe. .NET-land nennt sie Werttypen (im Gegensatz zu Referenztypen). Java-Leute nennen sie primitive Typen. Haskellions nennen sie einfach unboxed.

** Ich konzentriere mich in dieser Antwort auch auf Java, Haskell und C #, weil ich das weiß. Python, Ruby und Javascript haben ausschließlich Box-Werte. Dies wird auch als "Alles ist ein Objekt" -Ansatz *** bezeichnet.

*** Vorsichtsmaßnahme: Ein ausreichend fortgeschrittener Compiler / JIT kann in einigen Fällen tatsächlich erkennen, dass ein Wert, der beim Betrachten der Quelle semantisch eingerahmt ist, zur Laufzeit sicher ein Wert ohne Box sein kann. Im Wesentlichen sind Ihre Boxen dank brillanter Sprachimplementierer manchmal kostenlos.


Warum, obwohl ein Boxwert, welchen Nutzen hat die CLR oder was auch immer Form Boxing-Werte?
PositiveGuy

Kurz gesagt (ha ha), sie sind nur ein weiteres Objekt, was sehr praktisch ist. Grundelemente (zumindest in Java) stammen nicht von Object ab, können keine Felder haben, keine Methoden haben und verhalten sich im Allgemeinen ganz anders als andere Wertetypen. Andererseits kann die Arbeit mit ihnen sehr schnell und platzsparend sein. Also der Kompromiss.
Peter Burns

2
Javascript hat sogenannte typisierte Arrays (neues UInt32Array usw.), die Arrays von Ints und Floats ohne Box sind.
Nponeccop


72

Beim Boxen und Entpacken wird ein primitiver Wert in eine objektorientierte Wrapper-Klasse konvertiert (Boxen) oder ein Wert von einer objektorientierten Wrapper-Klasse zurück in den primitiven Wert konvertiert (Unboxing).

In Java müssen Sie beispielsweise möglicherweise einen intWert in ein Integer(Boxing) konvertieren, wenn Sie ihn in einem speichern möchten, Collectionda Grundelemente nicht in einem Collectioneinzigen Objekt gespeichert werden können . Aber wenn Sie es wieder aus dem Collectionherausholen möchten, möchten Sie möglicherweise den Wert als intund nicht als erhalten, Integerdamit Sie ihn entpacken.

Boxen und Unboxen ist nicht von Natur aus schlecht , aber es ist ein Kompromiss. Abhängig von der Sprachimplementierung kann es langsamer und speicherintensiver sein als nur die Verwendung von Grundelementen. Möglicherweise können Sie jedoch auch Datenstrukturen auf höherer Ebene verwenden und eine größere Flexibilität in Ihrem Code erzielen.

Heutzutage wird es am häufigsten im Zusammenhang mit der "Autoboxing / Autounboxing" -Funktion von Java (und anderen Sprachen) diskutiert. Hier ist eine Java-zentrierte Erklärung für Autoboxing .


23

In .Net:

Oft können Sie sich nicht darauf verlassen, welchen Variablentyp eine Funktion verbraucht. Daher müssen Sie eine Objektvariable verwenden, die sich vom kleinsten gemeinsamen Nenner aus erstreckt - in .Net ist dies object.

Ist objectjedoch eine Klasse und speichert ihren Inhalt als Referenz.

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

Während beide die gleichen Informationen enthalten, ist die zweite Liste größer und langsamer. Jeder Wert in der zweiten Liste ist tatsächlich eine Referenz auf einen object, der die enthält int.

Dies wird als Box bezeichnet, da das intvon der umwickelt wird object. Wenn es zurückgeworfen wird, wird das nicht intverpackt - wieder in seinen Wert konvertiert.

Für Werttypen (dh alle structs) ist dies langsam und benötigt möglicherweise viel mehr Speicherplatz.

Für Referenztypen (dh alle classes) ist dies weitaus weniger problematisch, da sie ohnehin als Referenz gespeichert werden.

Ein weiteres Problem mit einem Boxed-Value-Typ ist, dass es nicht offensichtlich ist, dass Sie sich mit der Box und nicht mit dem Wert befassen. Wenn Sie zwei vergleichen, vergleichen structsSie Werte, aber wenn Sie zwei vergleichen, vergleichen Sie classes(standardmäßig) die Referenz - dh handelt es sich um dieselbe Instanz?

Dies kann beim Umgang mit Boxed-Value-Typen verwirrend sein:

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

Es ist einfach zu umgehen:

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

Es ist jedoch eine andere Sache, auf die Sie beim Umgang mit Boxwerten achten müssen.


1
In vb.net ist die Unterscheidung zwischen Gleichheitssemantik klarer, Objectimplementiert den Gleichheitsoperator nicht, aber Klassentypen können mit dem IsOperator verglichen werden. kann umgekehrt Int32mit dem Gleichheitsoperator verwendet werden, aber nicht Is. Diese Unterscheidung macht viel klarer, welche Art von Vergleich durchgeführt wird.
Supercat

4

Boxingist der Prozess der Konvertierung eines Werttyps in einen Referenztyp. Während Unboxingdie Umwandlung eines Referenztyps in einen Werttyp ist.

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

Werttypen sind: int, charund structures, enumerations. Referenztypen sind: Classes, interfaces, arrays, stringsundobjects


3

Die generischen .NET FCL-Sammlungen:

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

wurden alle entwickelt, um die Leistungsprobleme beim Ein- und Auspacken in früheren Sammlungsimplementierungen zu überwinden.

Weitere Informationen finden Sie in Kapitel 16, CLR über C # (2. Ausgabe) .


1

Durch das Ein- und Auspacken können Werttypen als Objekte behandelt werden. Boxen bedeutet, einen Wert in eine Instanz des Objektreferenztyps zu konvertieren. Ist beispielsweise Inteine Klasse und intein Datentyp. Das Konvertieren intin Intist ein Beispiel für Boxen, während das Konvertieren Intin intUnboxing ist. Das Konzept hilft bei der Speicherbereinigung, Unboxing hingegen konvertiert den Objekttyp in den Werttyp.

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

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

Gibt in Javascript var ii = 123; typeof ii zurück number. var iiObj = new Number(123); typeof iiObjkehrt zurück object. typeof ii + iiObjkehrt zurück number. Das ist also das Javascript-Äquivalent zum Boxen. Der Wert iiObj wurde automatisch in eine primitive Zahl (ohne Box) konvertiert, um die Arithmetik durchzuführen und einen Wert ohne Box zurückzugeben.
PatS

-2

Wie alles andere kann Autoboxing problematisch sein, wenn es nicht sorgfältig verwendet wird. Der Klassiker besteht darin, eine NullPointerException zu erhalten und diese nicht aufzuspüren. Auch mit einem Debugger. Versuche dies:

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;
    }
}

Dies ist nur schlechter Code und hat nichts mit Autoboxing zu tun. Die Variable iwird vorzeitig initialisiert. Machen Sie es entweder zu einer leeren Deklaration ( Integer i;), damit der Compiler darauf hinweisen kann, dass Sie vergessen haben, es zu initialisieren, oder warten Sie, bis Sie den Wert kennen.
Erickson

Hmm, und wenn ich etwas dazwischen in einem try catch-Block mache, zwingt mich der Compiler, es mit etwas zu initialisieren. Dies ist kein richtiger Code - es ist ein Beispiel dafür, wie es passieren könnte.
PEELY

Was zeigt das? Es gibt absolut keinen Grund, das Integer-Objekt zu verwenden. Stattdessen müssen Sie sich jetzt mit einem potenziellen NullPointer befassen.
Richard Clayton
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.