Mengapa Koleksi Java tidak secara langsung menyimpan jenis Primitif?

123

Koleksi Java hanya menyimpan Objek, bukan tipe primitif; bagaimanapun kita bisa menyimpan kelas pembungkus.

Mengapa kendala ini?

JavaUser
sumber
2
Batasan ini payah ketika Anda berurusan dengan primitif dan ingin menggunakan Antrian untuk mengirim dan tarif kirim Anda sangat cepat. Saya sedang menangani masalah autoboxing yang memakan waktu terlalu lama.
JPM
Secara teknis, tipe primitif adalah objek (contoh tunggal seperti itu tepatnya), mereka tidak didefinisikan oleh a class, melainkan oleh JVM. Pernyataan tersebut int i = 1mendefinisikan pointer ke instance tunggal dari objek yang didefinisikan intdi JVM, yang disetel ke nilai yang 1ditentukan di suatu tempat di JVM. Ya, pointer di Java - ini hanya disarikan dari Anda oleh implementasi bahasa. Primitif tidak dapat digunakan sebagai Generik karena predikat bahasa semua jenis generik harus dari supertipe Object- itulah mengapa A<?>dikompilasi ke A<Object>pada saat run-time.
Robert E Fry
1
Tipe primitif @RobertEFry bukanlah objek di Java, jadi semua yang Anda tulis tentang instance tunggal dan semacamnya pada dasarnya salah dan membingungkan. Saya sarankan membaca bab "Jenis, Nilai, dan Variabel" dari Spesifikasi Bahasa Java, yang mendefinisikan apa itu objek: "Objek (§4.3.1) adalah turunan yang dibuat secara dinamis dari jenis kelas atau larik yang dibuat secara dinamis. "
typeracer

Jawaban:

99

Itu adalah keputusan desain Java, dan beberapa orang menganggapnya sebagai kesalahan. Wadah menginginkan Objek dan primitif tidak berasal dari Objek.

Ini adalah satu tempat dimana desainer .NET belajar dari JVM dan menerapkan tipe nilai dan generik sehingga tinju dihilangkan dalam banyak kasus. Di CLR, container generik dapat menyimpan tipe nilai sebagai bagian dari struktur container yang mendasarinya.

Java memilih untuk menambahkan dukungan generik 100% di kompiler tanpa dukungan dari JVM. JVM apa adanya, tidak mendukung objek "non-objek". Java generics memungkinkan Anda untuk berpura-pura tidak ada pembungkus, tetapi Anda tetap membayar harga kinerja tinju. Ini PENTING untuk kelas program tertentu.

Tinju adalah kompromi teknis, dan saya merasa ini adalah detail implementasi yang bocor ke dalam bahasa. Autoboxing adalah gula sintaksis yang bagus, tetapi masih merupakan penalti kinerja. Jika ada, saya ingin kompilator memperingatkan saya saat kotak otomatis. (Sejauh yang saya tahu, mungkin sekarang, saya menulis jawaban ini di tahun 2010).

Penjelasan yang bagus tentang SO tentang tinju: Mengapa beberapa bahasa membutuhkan Boxing dan Unboxing?

Dan kritik terhadap generik Java: Mengapa beberapa orang mengklaim bahwa implementasi Java generik buruk?

Dalam pembelaan Java, mudah untuk melihat ke belakang dan mengkritik. JVM telah bertahan dalam ujian waktu, dan merupakan desain yang baik dalam banyak hal.

codenheim
sumber
6
Bukan kesalahan, pilihan yang dipilih dengan cermat yang saya yakin telah melayani Jawa dengan sangat baik.
DJClayworth
13
Sudah cukup kesalahan bahwa .NET belajar darinya dan menerapkan autoboxing sejak awal, dan obat generik di level VM tanpa overhead tinju. Upaya Java sendiri untuk mengoreksi hanyalah solusi tingkat sintaksis yang masih menderita akibat kinerja autoboxing vs. tidak ada tinju sama sekali. Implementasi Java telah menunjukkan kinerja yang buruk dengan struktur data yang besar.
codenheim
2
@mrjoltcola: IMHO, autoboxing default adalah kesalahan, tetapi seharusnya ada cara untuk menandai variabel dan parameter yang seharusnya secara otomatis mengotakkan nilai yang diberikan kepadanya. Bahkan sekarang, saya pikir harus ada cara yang ditambahkan untuk menentukan bahwa variabel atau parameter tertentu tidak boleh menerima nilai yang baru dikotak-otomatiskan [misalnya seharusnya legal untuk memberikan Object.ReferenceEqualsreferensi tipe Objectyang mengidentifikasi bilangan bulat kotak, tetapi seharusnya tidak legal untuk meneruskan nilai integer]. Pembongkaran otomatis Java adalah IMHO hanya buruk.
supercat
18

Membuat implementasi lebih mudah. Karena primitif Java tidak dianggap sebagai Objek, Anda perlu membuat kelas koleksi terpisah untuk masing-masing primitif ini (tidak ada kode template untuk dibagikan).

Anda dapat melakukannya, tentu saja, lihat saja GNU Trove , Apache Commons Primitives, atau HPPC .

Kecuali jika Anda memiliki koleksi yang sangat besar, biaya overhead untuk pembungkus tidak cukup menjadi masalah bagi orang-orang untuk peduli (dan ketika Anda memiliki koleksi primitif yang sangat besar, Anda mungkin ingin menghabiskan usaha untuk melihat menggunakan / membangun struktur data khusus untuk mereka ).

Thilo
sumber
11

Ini kombinasi dari dua fakta:

  • Tipe primitif Java bukan tipe referensi (misalnya an intbukan Object)
  • Java melakukan generik menggunakan tipe-erasure dari tipe referensi (misalnya a List<?>benar-benar List<Object>pada saat run-time)

Karena keduanya benar, koleksi Java generik tidak dapat menyimpan tipe primitif secara langsung. Untuk kenyamanan, autoboxing diperkenalkan untuk memungkinkan tipe primitif otomatis dimasukkan kotak sebagai tipe referensi. Namun, jangan salah, koleksi tersebut tetap menyimpan referensi objek.

Bisakah ini dihindari? Mungkin.

  • Jika intadalah Object, maka tidak perlu untuk jenis kotak sama sekali.
  • Jika obat generik tidak selesai menggunakan penghapus tipe, maka primitif bisa digunakan untuk parameter tipe.
poligenelubricants
sumber
8

Ada konsep auto-boxing dan auto-unboxing. Jika Anda mencoba untuk menyimpan intdi List<Integer>kompiler Java akan secara otomatis mengubahnya menjadi file Integer.

Jeremy
sumber
1
Autoboxing diperkenalkan di Java 1.5 bersama dengan Generik.
Jeremy
1
Tapi itu adalah waktu kompilasi; gula sintaks tanpa manfaat kinerja. Autobox compiler Java, oleh karena itu hukuman kinerja dibandingkan dengan implementasi VM seperti .NET yang generiknya tidak melibatkan tinju.
codenheim
1
@mrjoltcola: Apa maksud Anda? Saya hanya berbagi fakta, bukan membuat argumen.
Jeremy
3
Maksud saya adalah penting untuk menunjukkan perbedaan antara sintaks dan kinerja. Saya juga menganggap komentar saya berbagi fakta, bukan argumen. Terima kasih.
codenheim
2

Ini bukan kendala, kan?

Pertimbangkan jika Anda ingin membuat koleksi yang menyimpan nilai primitif. Bagaimana Anda akan menulis koleksi yang dapat menyimpan int, atau float atau char? Kemungkinan besar Anda akan mendapatkan banyak koleksi, jadi Anda akan membutuhkan intlist dan charlist, dll.

Memanfaatkan sifat berorientasi objek dari Java saat Anda menulis kelas koleksi, ia dapat menyimpan objek apa pun sehingga Anda hanya memerlukan satu kelas koleksi. Ide ini, polimorfisme, sangat kuat dan sangat menyederhanakan desain perpustakaan.

Vincent Ramdhanie
sumber
7
"Bagaimana Anda akan menulis koleksi yang bisa menyimpan int, atau float atau char?" - Dengan generik / template yang diimplementasikan dengan benar seperti bahasa lain yang tidak membayar penalti dengan berpura-pura semuanya adalah objek.
codenheim
Saya hampir tidak pernah dalam enam tahun di Jawa ingin menyimpan koleksi primitif. Bahkan dalam beberapa kasus di mana saya mungkin ingin waktu ekstra dan biaya ruang untuk menggunakan objek referensi telah diabaikan. Secara khusus saya menemukan bahwa banyak orang berpikir mereka menginginkan Map <int, T>, lupa bahwa sebuah array akan melakukan trik itu dengan sangat baik.
DJClayworth
2
@DJClayworth Itu hanya bekerja dengan baik jika nilai kuncinya non-sparse. Tentu saja, Anda dapat menggunakan array tambahan untuk melacak kunci, tetapi itu memiliki masalah sendiri: akses yang relatif efisien akan memerlukan kedua larik diurutkan berdasarkan urutan kunci untuk memungkinkan pencarian biner, yang pada gilirannya akan membuat penyisipan dan penghapusan tidak efisien kecuali penyisipan / penghapusan berpola sedemikian rupa sehingga item yang disisipkan kemungkinan besar akan berakhir di tempat item yang sebelumnya dihapus dan / atau beberapa buffer diselingi dalam larik, dll. Ada sumber daya yang tersedia, tetapi akan menyenangkan untuk memilikinya di Java itu sendiri.
JAB
@JAB Sebenarnya ini berfungsi dengan baik jika kuncinya jarang, hanya membutuhkan lebih banyak memori daripada jika mereka tidak jarang. Dan jika kunci mereka jarang, itu berarti jumlahnya tidak banyak dan menggunakan Integer sebagai kunci berfungsi dengan baik. Gunakan pendekatan mana pun yang membutuhkan paling sedikit memori. Atau mana pun yang Anda rasa jika Anda tidak peduli.
DJClayworth
0

Saya pikir kita mungkin melihat kemajuan dalam ruang ini di JDK mungkin di Java 10 berdasarkan JEP ini - http://openjdk.java.net/jeps/218 .

Jika Anda ingin menghindari tinju primitif dalam koleksi saat ini, ada beberapa alternatif pihak ketiga. Selain opsi pihak ketiga yang disebutkan sebelumnya, ada juga Koleksi Eclipse , FastUtil , dan Koloboke .

Perbandingan peta primitif juga diterbitkan beberapa waktu lalu dengan judul: Ikhtisar HashMap Besar: JDK, FastUtil, Goldman Sachs, HPPC, Koloboke, Trove . Koleksi GS Collections (Goldman Sachs) telah dimigrasi ke Eclipse Foundation dan sekarang menjadi Eclipse Collections.

Donald Raab
sumber
0

Alasan utamanya adalah strategi desain java. ++ 1) Koleksi membutuhkan objek untuk manipulasi dan primitif bukan berasal dari objek sehingga ini bisa menjadi alasan lain. 2) tipe data primitif Java bukan tipe referensi untuk ex. int bukanlah sebuah obyek.

Untuk mengatasi:-

kami memiliki konsep auto-boxing dan auto-unboxing. jadi jika Anda mencoba untuk menyimpan tipe data primitif, kompilator akan secara otomatis mengubahnya menjadi objek dari kelas data primitif tersebut.

pengguna5693566
sumber