Apa itu tinju dan unboxing dan apa trade offnya?

135

Saya mencari jawaban yang jelas, singkat dan akurat.

Idealnya sebagai jawaban aktual, meskipun tautan ke penjelasan yang baik disambut.

Keith
sumber
2
Apakah ini benar-benar agnostik bahasa?
Henk Holterman
3
@HenkHolterman jelas tidak spesifik untuk bahasa, meskipun juga tidak relevan untuk semua bahasa - perbedaannya tidak akan relevan untuk sebagian besar bahasa yang diketik secara dinamis, misalnya. Saya tidak yakin tag apa yang bisa digunakan - language-but-not-type-agnostic? static-language-agnostic? Saya tidak yakin bahwa SO membutuhkan perbedaan; mungkin pertanyaan yang bagus untuk meta.
Keith

Jawaban:

189

Nilai kotak adalah struktur data yang pembungkus minimal di sekitar tipe primitif *. Nilai kotak biasanya disimpan sebagai pointer ke objek di heap .

Dengan demikian, nilai-nilai kotak menggunakan lebih banyak memori dan mengambil minimal dua pencarian memori untuk mengakses: satu untuk mendapatkan pointer, dan satu lagi untuk mengikuti pointer itu ke primitif. Jelas ini bukan hal yang Anda inginkan dalam lingkaran batin Anda. Di sisi lain, nilai kotak biasanya bermain lebih baik dengan tipe lain dalam sistem. Karena mereka adalah struktur data kelas satu dalam bahasa, mereka memiliki metadata yang diharapkan dan struktur yang dimiliki oleh struktur data lainnya.

Di Java dan Haskell, koleksi umum tidak dapat berisi nilai yang tidak dikotak. Koleksi umum di .NET dapat menyimpan nilai yang tidak dikotak tanpa hukuman. Di mana generik Java hanya digunakan untuk pengecekan tipe waktu-kompilasi, .NET akan menghasilkan kelas-kelas khusus untuk setiap tipe generik yang dipakai saat run time .

Java dan Haskell memiliki larik tanpa kotak, tetapi mereka jelas kurang nyaman daripada koleksi lainnya. Namun, ketika kinerja puncak diperlukan, ada baiknya sedikit ketidaknyamanan untuk menghindari overhead tinju dan unboxing.

* Untuk diskusi ini, nilai primitif adalah apa pun yang dapat disimpan di tumpukan panggilan , alih-alih disimpan sebagai penunjuk ke nilai di heap. Seringkali itu hanya tipe mesin (int, float, dll), struct, dan terkadang array berukuran statis. .NET-land menyebutnya tipe nilai (berbeda dengan tipe referensi). Orang Jawa menyebutnya tipe primitif. Haskellions hanya memanggilnya tanpa kotak.

** Saya juga fokus pada Java, Haskell, dan C # dalam jawaban ini, karena itulah yang saya tahu. Untuk apa nilainya, Python, Ruby, dan Javascript semua memiliki nilai kotak khusus. Ini juga dikenal sebagai pendekatan "Semuanya adalah objek".

*** Peringatan: Kompiler / JIT yang cukup canggih dalam beberapa kasus dapat benar-benar mendeteksi bahwa nilai yang kotak semantik ketika melihat sumbernya, dapat dengan aman menjadi nilai yang tidak kotak pada saat runtime. Intinya, terima kasih kepada pelaksana bahasa yang brilian, kotak Anda terkadang gratis.

Peter Burns
sumber
Mengapa meskipun nilai kotak, apa manfaat CLR atau apa pun mendapatkan nilai bentuk tinju?
PositiveGuy
Singkatnya (ha ha), mereka hanyalah objek lain, yang sangat nyaman. Primitif (setidaknya di Jawa) tidak turun dari Object, tidak dapat memiliki bidang, tidak dapat memiliki metode, dan secara umum berperilaku sangat berbeda dari jenis nilai lainnya. Di sisi lain, bekerja dengan mereka bisa sangat cepat dan hemat ruang. Dengan demikian trade off.
Peter Burns
2
Javascript disebut array yang diketik (UInt32Array baru, dll.) Yang merupakan array dari int dan float yang tidak dikotak.
nponeccop
126

dari C # 3.0 Singkatnya :

Boxing adalah tindakan casting tipe nilai ke dalam tipe referensi:

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

unboxing adalah ... kebalikannya:

// unboxing o
object o = 9; 
int x = (int)o; 
Christian Hagelid
sumber
72

Boxing & unboxing adalah proses mengubah nilai primitif menjadi kelas pembungkus berorientasi objek (tinju), atau mengubah nilai dari kelas pembungkus berorientasi objek kembali ke nilai primitif (unboxing).

Sebagai contoh, di java, Anda mungkin perlu mengubah intnilai menjadi Integer(tinju) jika Anda ingin menyimpannya di Collectionkarena primitif tidak dapat disimpan dalam objek Collection, hanya. Tetapi ketika Anda ingin mendapatkannya kembali dari CollectionAnda mungkin ingin mendapatkan nilai sebagai intdan bukan Integerjadi Anda akan membuka kotaknya.

Boxing dan unboxing pada dasarnya tidak buruk , tetapi merupakan tradeoff. Tergantung pada implementasi bahasa, itu bisa lebih lambat dan lebih banyak memori intensif daripada hanya menggunakan primitif. Namun, ini juga memungkinkan Anda untuk menggunakan struktur data tingkat yang lebih tinggi dan mencapai fleksibilitas yang lebih besar dalam kode Anda.

Saat ini, ini paling sering dibahas dalam konteks fitur Java (dan bahasa lainnya) "autoboxing / autounboxing". Berikut ini adalah penjelasan java centric tentang autoboxing .

Justin Standard
sumber
23

Di .Net:

Seringkali Anda tidak dapat bergantung pada jenis variabel yang akan dikonsumsi suatu fungsi, jadi Anda perlu menggunakan variabel objek yang memanjang dari penyebut umum terendah - di .Net ini object.

Namun objectadalah kelas dan menyimpan isinya sebagai referensi.

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

Meskipun keduanya memiliki informasi yang sama, daftar kedua lebih besar dan lebih lambat. Setiap nilai dalam daftar kedua sebenarnya adalah referensi ke objectyang memiliki int.

Ini disebut kotak karena intdibungkus oleh object. Ketika dilemparkan kembali, intkotaknya tidak - dikonversi kembali ke nilainya.

Untuk tipe nilai (yaitu semua structs) ini lambat, dan berpotensi menggunakan lebih banyak ruang.

Untuk tipe referensi (yaitu semua classes), ini jauh dari masalah, karena mereka tetap disimpan sebagai referensi.

Masalah lebih lanjut dengan tipe nilai kotak adalah bahwa tidak jelas bahwa Anda berurusan dengan kotak, bukan nilai. Ketika Anda membandingkan dua structsmaka Anda membandingkan nilai, tetapi ketika Anda membandingkan dua classeslalu (secara default) Anda membandingkan referensi - yaitu apakah ini contoh yang sama?

Ini bisa membingungkan ketika berhadapan dengan tipe nilai kotak:

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

Sangat mudah untuk dikerjakan:

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

Namun itu adalah hal lain yang harus diperhatikan ketika berhadapan dengan nilai-nilai kotak.

Keith
sumber
1
Di vb.net, perbedaan antara semantik kesetaraan lebih jelas, Objecttidak mengimplementasikan operator kesetaraan, tetapi tipe kelas dapat dibandingkan dengan Isoperator; sebaliknya, Int32dapat digunakan dengan operator kesetaraan, tetapi tidak Is. Perbedaan itu membuatnya jauh lebih jelas jenis perbandingan apa yang sedang dilakukan.
supercat
4

Boxingadalah proses konversi tipe nilai menjadi tipe referensi. Sedangkan Unboxingkonversi dari tipe referensi menjadi tipe nilai.

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

Jenis nilai adalah: int, chardan structures, enumerations. Jenis referensi adalah: Classes, interfaces, arrays, stringsdanobjects

vani
sumber
3

Koleksi generik .NET FCL:

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

semuanya dirancang untuk mengatasi masalah kinerja tinju dan unboxing dalam implementasi pengumpulan sebelumnya.

Untuk lebih lanjut, lihat bab 16, CLR via C # (Edisi ke-2) .

Jonathan Webb
sumber
1

Boxing dan unboxing memfasilitasi tipe nilai untuk diperlakukan sebagai objek. Tinju berarti mengonversi nilai menjadi turunan dari jenis referensi objek. Sebagai contoh, Intadalah kelas dan inttipe data. Konversi intke Intadalah contoh tinju, sedangkan konversi Intke intadalah unboxing. Konsep ini membantu dalam pengumpulan sampah, di Unboxing, di sisi lain, mengubah tipe objek menjadi tipe nilai.

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

o=123;
i=(int)o; //Unboxing.
Sanjay Kumar
sumber
Dalam javascript, var ii = 123; typeof ii kembali number. var iiObj = new Number(123); typeof iiObjkembali object. typeof ii + iiObjkembali number. Jadi ini setara dengan javascript tinju. Nilai iiObj secara otomatis dikonversi ke nomor primitif (tanpa kotak) untuk melakukan aritmatika dan mengembalikan nilai tanpa kotak.
PatS
-2

Seperti yang lainnya, autoboxing bisa bermasalah jika tidak digunakan dengan hati-hati. Klasik adalah berakhir dengan NullPointerException dan tidak dapat melacaknya. Bahkan dengan debugger. Coba ini:

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;
    }
}
PEELY
sumber
Ini hanya kode yang buruk, dan tidak ada hubungannya dengan autoboxing. Variabel idiinisialisasi sebelum waktunya. Entah menjadikannya deklarasi kosong ( Integer i;) sehingga kompiler dapat menunjukkan bahwa Anda lupa untuk menginisialisasi, atau menunggu untuk mendeklarasikannya sampai Anda tahu nilainya.
erickson
Hmm, dan jika saya melakukan sesuatu di antara di dalam blok coba tangkap maka kompiler akan memaksa saya untuk menginisialisasi dengan sesuatu. Ini bukan kode nyata - ini adalah contoh bagaimana hal itu bisa terjadi.
PEELY
Apa yang ditunjukkan ini? Sama sekali tidak ada alasan untuk menggunakan objek Integer. Sebaliknya Anda sekarang harus berurusan dengan NullPointer potensial.
Richard Clayton