Apa pustaka Koleksi Java yang paling efisien? [Tutup]

135

Apa pustaka Koleksi Java yang paling efisien?

Beberapa tahun yang lalu, saya melakukan banyak Java dan mendapat kesan saat itu bahwa trove adalah implementasi Java Collections terbaik (paling efisien). Tetapi ketika saya membaca jawaban atas pertanyaan “ Perpustakaan Java gratis yang paling berguna? ” Saya melihat bahwa harta karun hampir tidak disebutkan. Jadi perpustakaan Koleksi Java mana yang terbaik sekarang?

PEMBARUAN: Untuk memperjelas, saya sebagian besar ingin tahu perpustakaan apa yang akan digunakan ketika saya harus menyimpan jutaan entri dalam tabel hash, dll. (Memerlukan runtime kecil dan jejak memori).

jujur
sumber
Apa kunci dan nilai dalam tabel ini? Jika mereka tidak primitif, apa yang salah dengan HashMap biasa, dll?
Jon Skeet
Untuk peta yang sangat besar Anda mungkin menginginkan implementasi probing, atau bahkan sebaris seperti tabel database.
Tom Hawtin - tackline
1
Menariknya, saya tidak melihat Colt disebutkan di sini yang kemudian dimasukkan ke dalam Mahout.
smartnut007
4
Perlu disebutkan perpustakaan koleksi yang sangat bagus - Koleksi GS (github.com/goldmansachs/gs-collections). Ini memiliki dokumentasi yang sangat baik dan kumpulan lengkap dari koleksi yang dapat berubah dan tidak dapat diubah
Piotr Kochański

Jawaban:

73

Dari pemeriksaan, tampaknya Trove hanyalah perpustakaan koleksi untuk tipe primitif - ini tidak dimaksudkan untuk menambahkan banyak fungsionalitas di atas koleksi normal di JDK.

Secara pribadi (dan saya bias) saya suka Guava (termasuk proyek Koleksi Google Java sebelumnya). Itu membuat berbagai tugas (termasuk koleksi) jauh lebih mudah, dengan cara yang setidaknya cukup efisien. Mengingat bahwa operasi pengumpulan jarang membentuk hambatan dalam kode saya (menurut pengalaman saya) ini "lebih baik" daripada API koleksi yang mungkin lebih efisien tetapi tidak membuat kode saya dapat dibaca.

Mengingat bahwa tumpang tindih antara Trove dan Guava hampir nihil, mungkin Anda dapat menjelaskan apa yang sebenarnya Anda cari dari perpustakaan koleksi.

Jon Skeet
sumber
3
@ Andreas: Tidak bisa mengatakan saya setuju. Bukan karena itu skenario "satu atau yang lain" - Saya menggunakan koleksi biasa (dengan pembantu seperti kelas Daftar) dan kemudian menggunakan Iterables dll ketika saya perlu. Gunakan kompleksitas hanya jika itu membantu Anda.
Jon Skeet
10
setelah membaca komentar saya sendiri beberapa bulan setelah menggunakan GC secara ekstensif - Saya tidak setuju dengan pendapat saya sebelumnya, dan setuju sepenuhnya dengan pendapat Anda. menggunakan metode helper / kelas secara ekstensif, mereka membuat banyak kode lebih mudah dibaca dan lebih aman.
Andreas Petersson
1
@Andreas: Terima kasih telah kembali dan mengatakan begitu - Saya senang mendengar bahwa GJC membantu :)
Jon Skeet
2
Hai, Jon, Koleksi Google Java sekarang menjadi Guava . Anda mungkin ingin memperbarui posting Anda untuk referensi di masa mendatang :)
Artur Czajka
1
Saya telah mengerjakan beberapa proyek intensif data di mana koleksinya menjadi hambatan besar. Koleksi Java sangat tidak efisien (baik memori dan kecepatan) terutama jika mereka menyimpan primitif.
Jay Askren
104

Pertanyaannya adalah (sekarang) tentang menyimpan banyak data, yang dapat direpresentasikan menggunakan tipe primitif seperti int, dalam Peta. Beberapa jawaban di sini sangat menyesatkan menurut saya. Mari kita lihat alasannya.

Saya memodifikasi benchmark dari trove untuk mengukur baik runtime maupun konsumsi memori. Saya juga menambahkan PCJ ke benchmark ini, yang merupakan pustaka koleksi lain untuk tipe primitif (saya menggunakannya secara ekstensif). Tolok ukur harta 'resmi' tidak membandingkan IntIntMaps dengan Java Collection Map<Integer, Integer>, mungkin penyimpanan Integersdan penyimpanan intstidak sama dari sudut pandang teknis. Tetapi pengguna mungkin tidak peduli dengan detail teknis ini, dia ingin menyimpan data yang dapat direpresentasikan dengan intsefisien.

Pertama, bagian kode yang relevan:

new Operation() {

     private long usedMem() {
        System.gc();
        return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
     }

     // trove
     public void ours() {
        long mem = usedMem();
        TIntIntHashMap ours = new TIntIntHashMap(SET_SIZE);
        for ( int i = dataset.size(); i-- > 0; ) {
           ours.put(i, i);
        }
        mem = usedMem() - mem;
        System.err.println("trove " + mem + " bytes");
        ours.clear();
     }

     public void pcj() {
        long mem = usedMem();
        IntKeyIntMap map = new IntKeyIntOpenHashMap(SET_SIZE);
        for ( int i = dataset.size(); i-- > 0; ) {
           map.put(i, i);
        }
        mem = usedMem() - mem;
        System.err.println("pcj " + mem + " bytes");
        map.clear();
     }

     // java collections
     public void theirs() {
        long mem = usedMem();
        Map<Integer, Integer> map = new HashMap<Integer, Integer>(SET_SIZE);
        for ( int i = dataset.size(); i-- > 0; ) {
           map.put(i, i);
        }
        mem = usedMem() - mem;
        System.err.println("java " + mem + " bytes");
        map.clear();
     }

Saya menganggap datanya primitif ints, yang tampaknya waras. Tapi ini menyiratkan hukuman waktu proses untuk java util, karena auto-boxing, yang tidak diperlukan untuk framework koleksi primitif.

Hasil runtime (tanpa gc()panggilan, tentu saja) di WinXP, jdk1.6.0_10:

                      100000 operasi put 100000 berisi operasi 
koleksi java 1938 ms 203 ms
harta karun 234 ms 125 ms
pcj 516 ms 94 ms

Meskipun ini mungkin sudah tampak drastis, ini bukanlah alasan untuk menggunakan kerangka kerja seperti itu.

Alasannya adalah kinerja memori. Hasil untuk Peta yang berisi 100000 intentri:

koleksi java berosilasi antara 6644536 dan 7168840 byte
harta karun 1853296 byte
pcj 1866112 byte

Koleksi Java membutuhkan lebih dari tiga kali memori dibandingkan dengan kerangka kerja koleksi primitif. Yaitu Anda dapat menyimpan data tiga kali lebih banyak dalam memori, tanpa menggunakan disk IO yang menurunkan kinerja runtime menurut besarnya. Dan ini penting. Baca skalabilitas tinggi untuk mencari tahu alasannya.

Menurut pengalaman saya, konsumsi memori yang tinggi adalah masalah kinerja terbesar dengan Java, yang tentu saja menghasilkan kinerja runtime yang lebih buruk juga. Kerangka kerja koleksi primitif dapat sangat membantu di sini.

Jadi: Tidak, java.util bukanlah jawabannya. Dan "menambahkan fungsionalitas" ke koleksi Java bukanlah intinya ketika bertanya tentang efisiensi. Juga koleksi JDK modern tidak "mengungguli bahkan koleksi Trove khusus".

Penafian: Tolok ukur di sini masih jauh dari lengkap, juga tidak sempurna. Ini dimaksudkan untuk menyampaikan poin, yang telah saya alami dalam banyak proyek. Koleksi primitif cukup berguna untuk mentolerir API mencurigakan - jika Anda bekerja dengan banyak data.

the.duckman
sumber
3
Sebenarnya, menurut saya jawaban Anda menyesatkan. Menyimpan ints vs Integers sangat berbeda, dan kemungkinan besar merupakan alasan utama peningkatan penggunaan memori. Saya setuju kerangka pengumpulan tipe mentah dapat berguna, tetapi tidak membuat trove atau pcj "lebih baik" daripada java.util.
Jorn
22
Pertanyaannya adalah tentang menyimpan data int secara efisien. Bukan tentang menyimpan Integer. Untuk tugas ini trove / pcj lebih efisien, seperti yang saya coba tunjukkan. Menggunakan Integer menyebabkan inefisiensi runtime dan memori. Karena java.util tidak mengizinkan penggunaan primitif, ini bukanlah pilihan terbaik untuk tugas ini.
the.duckman
2
(untuk komunitas Rusia) ini patokan lain: total-holywar.blogspot.com/2011/07/…
dma_k
Tidak yakin apakah kami tidak menggunakan int sebagai kunci, hanya String biasa. Apa hasil meja kerja untuk mereka?
Clark Bao
@ClarkBao (maaf terlambat) Menyimpan objek apapun sebagai kunci akan menggunakan objek tersebut hashCode(). Itu membuat Anda menjadi intkuncinya.
Matthieu
47

Saya tahu ini adalah posting lama dan ada banyak jawaban di sini. Tapi, Jawaban di atas dangkal dan terlalu disederhanakan dalam hal menyarankan perpustakaan. Tidak ada satu perpustakaan pun yang bekerja dengan baik di berbagai tolok ukur yang disajikan di sini. Satu-satunya kesimpulan yang saya peroleh adalah jika Anda peduli dengan kinerja dan memori dan secara khusus berurusan dengan tipe primitif, lebih dari layak untuk melihat alternatif non jdk.

Berikut adalah analisis yang lebih baik, dalam hal mekanisme benchmark dan perpustakaan yang tercakup. Ini adalah utas dalam daftar dev mahout.

Perpustakaan yang tercakup adalah

  • HPPC
  • Harta karun
  • FastUtil
  • Mahout (Colt)
  • Koleksi Java

Pembaruan Juni 2015 : Sayangnya, tolok ukur asli tidak lagi tersedia dan selain itu agak ketinggalan jaman. Berikut adalah tolok ukur yang cukup baru (Jan 2015) yang dilakukan oleh orang lain. Ini tidak selengkap dan tidak memiliki alat eksplorasi interaktif seperti tautan aslinya.

smartnut007
sumber
1
Terima kasih. Ini sangat membantu .. mengingat pentingnya pertanyaan, sulit untuk percaya bahwa tidak ada jawaban lain (selain the.duckman) yang benar-benar menjawab pertanyaan ini.
Dexter
20

Seperti yang diketahui oleh para komentator lain, definisi "efisien" mendapat banyak keuntungan. Namun belum ada yang menyebutkan perpustakaan Javolution .

Beberapa sorotan:

  • Kelas javolution cepat, sangat cepat (misalnya penyisipan / penghapusan teks di O [Log (n)] daripada O [n] untuk StringBuffer / StringBuilder standar).
  • Semua kelas Javolution kompatibel dengan waktu nyata dan memiliki perilaku yang sangat deterministik (dalam kisaran mikrodetik). Lebih jauh lagi (tidak seperti pustaka standar), Javolution adalah RTSJ aman (tidak ada benturan memori atau kebocoran memori saat digunakan dengan ekstensi Java Real-Time).
  • Kelas koleksi real-time Javolution (peta, daftar, tabel, dan set) dapat digunakan sebagai pengganti sebagian besar kelas koleksi standar dan menyediakan fungsionalitas tambahan.
  • Koleksi Javolution memberikan jaminan konkurensi untuk membuat implementasi algoritme paralel lebih mudah.

Distribusi Javolution menyertakan rangkaian benchmark sehingga Anda dapat melihat bagaimana mereka menumpuk terhadap perpustakaan lain / koleksi bawaan.

sstock
sumber
16

Beberapa koleksi libs untuk dipertimbangkan:

Pertama-tama saya akan meraih perpustakaan koleksi JDK. Ini mencakup hal-hal paling umum yang perlu Anda lakukan dan jelas sudah tersedia untuk Anda.

Koleksi Google mungkin adalah pustaka berkualitas tinggi terbaik di luar JDK. Ini banyak digunakan dan didukung dengan baik.

Apache Commons Collections lebih tua dan mengalami sedikit masalah "terlalu banyak juru masak" tetapi memiliki banyak hal yang berguna juga.

Trove memiliki koleksi yang sangat khusus untuk kasus seperti kunci / nilai primitif. Saat ini kami menemukan bahwa pada JDK modern dan dengan koleksi Java 5+ serta kasus penggunaan bersamaan, koleksi JDK lebih baik daripada koleksi Trove khusus.

Jika Anda memiliki kasus penggunaan konkurensi yang sangat tinggi, Anda harus memeriksa hal-hal seperti NonBlockingHashMap di lib berskala tinggi, yang merupakan implementasi bebas kunci dan dapat menginjak ConcurrentHashMap jika Anda memiliki kasus penggunaan yang tepat untuk itu.

Alex Miller
sumber
7
"Saat ini kami menemukan bahwa pada JDK modern dan dengan koleksi Java 5+ serta kasus penggunaan bersamaan, koleksi JDK bahkan lebih baik dari koleksi Trove khusus." Menyesatkan - Saya belum pernah melihat tolok ukur mikro di mana menyimpan / mengambil tipe primitif dalam kelas koleksi primitif khusus seperti Trove tidak mengungguli kelas koleksi JDK baik dalam penggunaan memori dan waktu CPU. Jika Anda menggunakan objek meskipun (dan bukan tipe primitif), maka saya setuju dengan Alex, resah atas koleksi impl bukanlah masalah besar.
Riyad Kalla
2
Pernyataan ini didasarkan pada penggunaan dunia nyata yang berat (yang akan saya ambil alih sebagai tolok ukur mikro setiap hari) dari berbagai implik koleksi di mana kami sebelumnya membutuhkan koleksi Trove tetapi sekarang dapat menariknya keluar. Pembaruan JDK 6 akhir (sekitar akhir 2009) sebenarnya menyediakan kode khusus untuk kunci peta umum seperti Integer yang secara substansial telah meningkatkan beberapa penggunaan yang paling umum.
Alex Miller
1
Alex, saya tidak ragu dalam kasus penggunaan spesifik Anda bahwa mengeluarkan koleksi primitif dan pergi dengan koleksi JDK cukup cepat, tetapi melambaikan tangan Anda melintasi lanskap yang merupakan koleksi dan berkata "Semua kamu yang lewat, itu cukup cepat! " tidak akurat. Jika saya mengerjakan mesin game 2D, overhead tinju / unboxing tipe primitif saya terus-menerus sangat mahal. Jika saya mengerjakan REST API maka tidak, itu mungkin tidak membuat perbedaan terukur sama sekali sehubungan dengan operasi yang jauh lebih mahal seperti HTTP I / O. Saya hanya merasa terdorong untuk mengukur posting Anda.
Riyad Kalla
4
Saya tidak berpikir siapa pun yang membaca ini harus mendengarkan salah satu dari kami. Mereka harus menguji kasus penggunaan mereka sendiri dan melihat mana yang memiliki kinerja terbaik. Komentar saya didasarkan pada pengujian kinerja tim saya yang cukup agresif dengan berbagai pustaka. YMMV.
Alex Miller
2
Saya setuju dengan @Riyad. Saya sedang menulis rangkaian automata berkinerja tinggi dan telah menerapkannya dengan Trove dan Java Collections Framework (pembaruan terbaru jdk 6). Trove mengungguli waktu besar. Dalam urutan puluhan kali lebih baik dalam kecepatan komputasi dan konsumsi memori.
Nico Huysamen
6

java.util

Maaf untuk jawaban yang jelas, tetapi untuk sebagian besar penggunaan, Koleksi Java default sudah lebih dari cukup.

Yuval Adam
sumber
4
Untuk penggunaan dasar, ya. Tapi saya pikir kerangka kerja melewatkan beberapa fitur dasar dan lanjutan (seperti koleksi yang tidak dapat diubah, filter, multimaps, dll) dan di situlah (misalnya) Koleksi Google masuk
Jorn
1
Saya pikir jawaban ini melenceng. JCF mungkin luar biasa pada tahun 2002 ketika orang tidak banyak menggunakan Java. Sayangnya itu belum berumur dengan baik, terutama jika dibandingkan dengan dukungan koleksi dari bahasa JVM lainnya.
Ted Pennings
3
-1 Pertanyaannya adalah "paling efisien untuk menyimpan int" dan setiap contoh yang disebutkan lebih baik daripada java.util
kommradHomer
6

Untuk menyimpan jutaan Stringdalam satu peta, lihat di http://code.google.com/p/flatmap

akuhn
sumber
3
+1 Dapatkah Anda memperkenalkan bagaimana itu ditingkatkan?
Clark Bao
1
Harus ada posting blog oleh penulis flatmap di suatu tempat di internet.
akuhn
4

Saya pengembang koleksi bahagia dari koleksi bahagia di source-forge

  1. Koleksi berbasis acara
  2. Tidak dapat dimodifikasi
  3. SortedList
  4. Cache
Andreas Hollmann
sumber
3

ConcurrentHashMap serta java.util.concurrentpaketnya harus disebutkan, jika Anda berencana menggunakan HashMap di banyak utas. footprint memori kecil diasumsikan, karena ini adalah bagian dari java standar.

Andreas Petersson
sumber
3

Tergantung bagaimana kita mendefinisikan "efisien".

Setiap struktur data memiliki perilaku Big-Oh sendiri untuk membaca, menulis, mengulang, footprint memori, dll. Daftar tertaut di satu pustaka kemungkinan besar sama dengan pustaka lainnya. Dan peta hash akan lebih cepat untuk membaca O (1) daripada daftar tertaut O (n).

Tetapi ketika saya membaca jawaban atas pertanyaan "Perpustakaan Java gratis yang paling berguna?" Saya perhatikan bahwa harta karun hampir tidak disebutkan.

Ini tidak terdengar seperti "paling efisien". Kedengarannya seperti "paling populer" bagi saya.

Hanya beberapa umpan balik - Saya belum pernah mendengarnya, dan saya tidak tahu siapa pun yang telah menggunakannya. Koleksi yang dibangun ke dalam JDK, Google, atau Apache Commons sudah saya kenal.

duffymo
sumber
3

Trove menawarkan beberapa keuntungan.

  • footprint memori yang lebih kecil, itu tidak menggunakan objek Map.Entry
  • Anda dapat menggunakan strategi hash alih-alih kunci untuk peta, ini menghemat memori dan berarti Anda tidak perlu menentukan kunci baru setiap kali Anda ingin menyimpan objek dalam cache pada set baru atributnya
  • itu memiliki jenis koleksi primitif
  • pikir itu memiliki beberapa bentuk iterator internal

Meskipun demikian, banyak yang telah dilakukan untuk meningkatkan koleksi jdk sejak trove ditulis.

Ini adalah strategi hashing yang membuatnya menarik bagi saya ... Google untuk mencari harta karun dan membaca ikhtisar mereka.

duffymo
sumber
2

Jika Anda ingin menyimpan jutaan record dalam tabel hash, kemungkinan besar Anda akan mengalami masalah memori. Ini terjadi pada saya ketika saya mencoba membuat peta dengan 2,3 juta objek String, misalnya. Saya memilih BerkeleyDB , yang sangat matang dan berkinerja baik. Mereka memiliki Java API yang membungkus Collections API, sehingga Anda dapat dengan mudah membuat peta besar yang sewenang-wenang dengan sedikit jejak memori. Akses akan lebih lambat (karena disimpan di disk).

Pertanyaan lanjutan : apakah ada perpustakaan yang layak (dan efisien), terpelihara dengan baik, untuk koleksi yang tidak dapat diubah? Clojure memiliki dukungan yang sangat baik untuk ini, dan alangkah baiknya memiliki sesuatu yang serupa untuk Java.

fred-o
sumber
1
Koleksi Google menambahkan Koleksi yang tidak dapat diubah.
the.duckman