Jika saya memiliki objek yang mengimplementasikan Map
antarmuka di Jawa dan saya ingin mengulangi setiap pasangan yang ada di dalamnya, apa cara paling efisien untuk menelusuri peta?
Apakah urutan elemen tergantung pada implementasi peta spesifik yang saya miliki untuk antarmuka?
java
dictionary
collections
iteration
iMack
sumber
sumber
Jawaban:
sumber
remove
metodenya. Jika itu masalahnya, jawaban lain ini menunjukkan kepada Anda bagaimana melakukannya. Jika tidak, loop yang ditingkatkan seperti yang ditunjukkan pada jawaban di atas adalah cara untuk pergi.map.values()
ataumap.keySet()
jika Anda ingin mengulang nilai atau kunci saja.Untuk meringkas jawaban lain dan menggabungkannya dengan apa yang saya ketahui, saya menemukan 10 cara utama untuk melakukan ini (lihat di bawah). Juga, saya menulis beberapa tes kinerja (lihat hasil di bawah). Sebagai contoh, jika kita ingin menemukan jumlah semua kunci dan nilai peta, kita dapat menulis:
Menggunakan iterator dan Map.Entry
Menggunakan foreach dan Map.Entry
Menggunakan forEach dari Java 8
Menggunakan keySet dan foreach
Menggunakan keySet dan iterator
Menggunakan untuk dan Peta. Coba
Menggunakan Java 8 Stream API
Menggunakan paralel Java 8 Stream API
Menggunakan IterableMap of
Apache Collections
Menggunakan koleksi MutableMap of Eclipse (CS)
Tes kinerja (mode = AverageTime, sistem = Windows 8.1 64-bit, Intel i7-4790 3.60 GHz, 16 GB)
Untuk peta kecil (100 elemen), skor 0,308 adalah yang terbaik
Untuk peta dengan 10.000 elemen, skor 37.606 adalah yang terbaik
Untuk peta dengan 100000 elemen, skor 1184.767 adalah yang terbaik
Grafik (tes kinerja tergantung pada ukuran peta)
Tabel (tes kinerja tergantung pada ukuran peta)
Semua tes ada di GitHub .
sumber
long sum = 0; map.forEach( /* accumulate in variable sum*/);
menangkapsum
panjang, yang mungkin lebih lambat daripada mengatakanstream.mapToInt(/*whatever*/).sum
misalnya. Tentu saja Anda tidak selalu dapat menghindari negara menangkap, tetapi itu mungkin merupakan tambahan yang masuk akal ke bangku cadanganAtomicInteger
memecahkan masalah.x±e
menyiratkan bahwa ada hasil dalam interval darix-e
kex+e
, sehingga hasil tercepat (1184.767±332.968
) berkisar dari852
ke1518
, sedangkan paling lambat kedua (1706.676±436.867
) berjalan antara1270
dan2144
, sehingga hasilnya masih tumpang tindih secara signifikan. Sekarang lihat hasil paling lambat3289.866±1445.564
, yang menyiratkan perbedaan antara1844
dan4735
dan Anda tahu bahwa hasil tes ini tidak ada artinya.while
vs.for
loop bukan teknik yang berbeda untuk iterasi. Dan saya terkejut mereka memiliki variasi di antara mereka dalam tes Anda - yang menunjukkan bahwa tes tidak terisolasi dengan baik dari faktor eksternal yang tidak terkait dengan hal-hal yang ingin Anda uji.Di Java 8 Anda dapat melakukannya dengan bersih dan cepat menggunakan fitur lambdas baru:
Jenis
k
danv
akan disimpulkan oleh kompiler dan tidak perlu digunakanMap.Entry
lagi.Mudah sekali!
sumber
map.entrySet().stream()
docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.htmlYa, urutannya tergantung pada implementasi Peta spesifik.
@ ScArcher2 memiliki sintaks Java 1.5 yang lebih elegan . Di 1.4, saya akan melakukan sesuatu seperti ini:
sumber
for
konstrukfor (Entry e : myMap.entrySet)
tidak akan memungkinkan Anda untuk mengubah koleksi, tetapi contoh seperti @HanuAthena menyebutkan itu harus berfungsi, karena memberi AndaIterator
ruang lingkup. (Kecuali saya kehilangan sesuatu ...)Entry thisEntry = (Entry) entries.next();
: tidak mengenaliEntry
. Apakah itu kodesemu untuk sesuatu yang lain?java.util.Map.Entry
.Kode umum untuk iterasi pada peta adalah:
HashMap
adalah implementasi peta kanonik dan tidak membuat jaminan (atau meskipun seharusnya tidak mengubah urutan jika tidak ada operasi yang bermutasi dilakukan di atasnya).SortedMap
akan mengembalikan entri berdasarkan urutan alami kunci, atauComparator
, jika disediakan.LinkedHashMap
akan mengembalikan entri dalam urutan penyisipan atau akses-urutan tergantung pada bagaimana hal itu telah dibangun.EnumMap
mengembalikan entri dalam urutan kunci.(Pembaruan: Saya pikir ini tidak lagi benar. ) Catatan,
IdentityHashMap
entrySet
iterator saat ini memiliki implementasi khusus yang mengembalikanMap.Entry
contoh yang sama untuk setiap item dientrySet
! Namun, setiap kali iterator baru dimajukanMap.Entry
diperbarui.sumber
LinkedHashMap
hitungan. Melalui orang-orangiterator
,spliterator
,entrySet
, dll, tidak mengubah pesanan.Contoh penggunaan iterator dan generik:
sumber
Iterator
for for untuk membatasi cakupannya.for (Iterator<Map.Entry<K, V>> entries = myMap.entrySet().iterator(); entries.hasNext(); ) { Map.Entry<K, V> entry = entries.next(); }
. Dengan menggunakan konstruk tersebut, kami membatasi ruang lingkup (visibilitas variabel)entries
ke for for.Ini adalah pertanyaan dua bagian:
Bagaimana cara mengulangi entri Peta - @ ScArcher2 telah menjawabnya dengan sempurna.
Apa urutan iterasi - jika Anda hanya menggunakan
Map
, maka secara tegas, tidak ada jaminan pemesanan . Jadi Anda tidak harus benar-benar mengandalkan pemesanan yang diberikan oleh implementasi apa pun. Namun,SortedMap
antarmuka meluasMap
dan memberikan apa yang Anda cari - implementasi akan memberikan urutan pengurutan yang konsisten.NavigableMap
adalah ekstensi lain yang berguna - ini adalahSortedMap
dengan metode tambahan untuk menemukan entri dengan posisi yang dipesan di set kunci. Jadi berpotensi ini bisa menghapus kebutuhan untuk iterasi di tempat pertama - Anda mungkin dapat menemukan spesifikentry
Anda setelah menggunakanhigherEntry
,lowerEntry
,ceilingEntry
, ataufloorEntry
metode. ThedescendingMap
Metode bahkan memberikan metode eksplisit membalik urutan traversal .sumber
Ada beberapa cara untuk beralih di peta.
Berikut adalah perbandingan kinerja mereka untuk kumpulan data umum yang disimpan di peta dengan menyimpan sejuta pasangan nilai kunci di peta dan akan beralih di atas peta.
1) Menggunakan
entrySet()
untuk setiap loop50 milidetik
2) Menggunakan
keySet()
untuk setiap loop76 milidetik
3) Menggunakan
entrySet()
dan iterator50 milidetik
4) Menggunakan
keySet()
dan iterator75 milidetik
Saya telah merujuk
this link
.sumber
Cara yang benar untuk melakukan ini adalah dengan menggunakan jawaban yang diterima karena merupakan yang paling efisien. Saya menemukan kode berikut terlihat sedikit lebih bersih.
sumber
O(1) = 2*O(1)
cukup banyak definisi notasi O besar. Anda benar dalam hal ini berjalan sedikit lebih lambat, tetapi dalam hal kompleksitas mereka sama.2
, karena iterasi padaentrySet()
tidak sama sekali; itu hanya traversal linier dari semua entri. Sebaliknya, iterasi di ataskeySet()
dan melakukan pencarian per kunci dikenakan satu pencarian per kunci, jadi kita berbicara tentang pencarian nol vs pencarian n di sini, dan menjadi ukuranMap
. Jadi faktornya jauh di luar2
...FYI, Anda juga dapat menggunakan
map.keySet()
danmap.values()
jika Anda hanya tertarik pada kunci / nilai peta dan bukan yang lain.sumber
Dengan Java 8 , Anda dapat mengulangi Peta menggunakan ekspresi forEach dan lambda,
sumber
Dengan Eclipse Collections , Anda akan menggunakan
forEachKeyValue
metode padaMapIterable
antarmuka, yang diwarisi olehMutableMap
danImmutableMap
antarmuka serta implementasinya.Menggunakan kelas dalam anonim, Anda dapat menulis kode sebagai berikut:
Catatan: Saya pengendara untuk Eclipse Collections.
sumber
Di Java 1.8 (Java 8) ini menjadi lebih mudah dengan menggunakan metode forEach dari operasi Agregat ( operasi Stream ) yang terlihat mirip dengan iterator dari Iterable Interface.
Cukup salin tempel pernyataan di bawah ini ke kode Anda dan ganti nama variabel HashMap dari hm ke variabel HashMap Anda untuk mencetak pasangan nilai kunci.
Di bawah ini adalah contoh kode yang saya coba gunakan Ekspresi Lambda . Barang ini sangat keren. Harus dicoba.
Anda juga dapat menggunakan Spliterator untuk hal yang sama.
MEMPERBARUI
Termasuk tautan dokumentasi ke Oracle Docs. Untuk lebih lanjut tentang Lambda buka tautan ini dan harus membaca Operasi Agregat dan untuk Spliterator buka tautan ini .
sumber
Secara teori, cara paling efisien akan tergantung pada implementasi Peta. Cara resmi untuk melakukan ini adalah dengan menelepon
map.entrySet()
, yang mengembalikan satu setMap.Entry
, yang masing-masing berisi kunci dan nilai (entry.getKey()
danentry.getValue()
).Dalam implementasi istimewa, mungkin ada bedanya apakah Anda menggunakan
map.keySet()
,map.entrySet()
atau sesuatu yang lain. Tapi saya tidak bisa memikirkan alasan mengapa ada orang yang menulis seperti itu. Kemungkinan besar tidak ada bedanya dengan kinerja apa yang Anda lakukan.Dan ya, urutannya akan tergantung pada implementasinya - serta (mungkin) urutan penyisipan dan faktor-faktor sulit dikendalikan lainnya.
[Sunting] Saya menulis
valueSet()
awalnya tetapi tentu sajaentrySet()
sebenarnya jawabannya.sumber
Java 8
Kami memiliki
forEach
metode yang menerima ekspresi lambda . Kami juga punya aliran API. Pertimbangkan sebuah peta:Iterate over keys:
Iterasikan lebih dari nilai:
Iterasi entri (Menggunakan forEach dan Streaming):
Keuntungan dengan stream adalah mereka dapat diparalelkan dengan mudah jika kita mau. Kami hanya perlu menggunakan
parallelStream()
di tempat distream()
atas.forEachOrdered
vsforEach
dengan stream? TidakforEach
mengikuti urutan perjumpaan (jika didefinisikan) dan secara inheren bersifat non-deterministik sedangkan yangforEachOrdered
tidak. JadiforEach
tidak menjamin bahwa pesanan akan disimpan. Periksa juga ini untuk lebih.sumber
Java 8:
Anda dapat menggunakan ekspresi lambda:
Untuk informasi lebih lanjut, ikuti ini .
sumber
myMap.forEach( (currentKey,currentValue) -> /* action */ );
jauh lebih ringkas.Coba ini dengan Java 1.4:
sumber
Dalam Peta seseorang dapat mengulangi
keys
dan / atauvalues
dan / atauboth (e.g., entrySet)
bergantung pada seseorang yang tertarik pada_ Suka:Iterate melalui
keys -> keySet()
peta:Iterate melalui
values -> values()
peta:Iterate melalui
both -> entrySet()
peta:Selain itu, ada 3 cara berbeda untuk Iterate Through a HashMap. Mereka seperti di bawah ini__
sumber
Paling ringkas dengan Java 8:
sumber
Jika Anda memiliki Peta umum yang belum diketik, Anda dapat menggunakan:
sumber
ATAU
sumber
Jika efisiensi pengulangan tombol adalah prioritas untuk aplikasi Anda, maka pilih
Map
implementasi yang mempertahankan kunci dalam urutan yang Anda inginkan.Ya, tentu saja.
Map
implementasi menjanjikan urutan iterasi tertentu, yang lain tidak.Map
berbeda mempertahankan urutan berbeda dari pasangan kunci-nilai.Lihat tabel ini yang saya buat merangkum berbagai
Map
implementasi yang dibundel dengan Java 11. Secara khusus, perhatikan kolom urutan iterasi . Klik / ketuk untuk memperbesar.Anda dapat melihat ada empat
Map
implementasi yang mempertahankan pesanan :TreeMap
ConcurrentSkipListMap
LinkedHashMap
EnumMap
NavigableMap
antarmukaDua dari mereka menerapkan
NavigableMap
antarmuka:TreeMap
&ConcurrentSkipListMap
.Yang lebih tua
SortedMap
Antarmuka yang secara efektif digantikan olehNavigableMap
antarmuka yang lebih baru . Tetapi Anda mungkin menemukan implementasi pihak ketiga yang mengimplementasikan antarmuka yang lebih lama saja.Tatanan alami
Jika Anda ingin
Map
pasangan yang mengatur pasangannya diatur oleh "urutan alami" kunci, gunakanTreeMap
atauConcurrentSkipListMap
. Istilah "tatanan alam" berarti kelas dari kunci yang diterapkanComparable
. Nilai yang dikembalikan olehcompareTo
metode ini digunakan untuk perbandingan dalam penyortiran.Pesanan kustom
Jika Anda ingin menentukan rutin penyortiran khusus untuk kunci Anda yang akan digunakan dalam mempertahankan pesanan yang diurutkan, berikan
Comparator
implementasi yang sesuai dengan kelas kunci Anda. Gunakan salah satuTreeMap
atauConcurrentSkipListMap
, lewatComparator
.Urutan penyisipan asli
Jika Anda ingin pasangan peta Anda disimpan dalam urutan aslinya tempat Anda memasukkannya ke dalam peta, gunakan
LinkedHashMap
.Urutan definisi-enum
Jika Anda menggunakan enum seperti
DayOfWeek
atauMonth
sebagai kunci Anda, gunakanEnumMap
kelas. Tidak hanya kelas ini sangat dioptimalkan untuk menggunakan memori sangat sedikit dan berjalan sangat cepat, ia mempertahankan pasangan Anda dalam urutan yang ditentukan oleh enum. UntukDayOfWeek
, misalnya, kunciDayOfWeek.MONDAY
akan menjadi yang pertama ditemukan ketika iterasi, dan kunciDayOfWeek.SUNDAY
akan menjadi yang terakhir.Pertimbangan lainnya
Dalam memilih
Map
implementasi, pertimbangkan juga:Collections::synchronizedMap
(kurang disukai).Kedua pertimbangan ini tercakup dalam tabel grafik di atas.
sumber
EnumMap
, karena ini adalah pertama kalinya saya mendengarnya. Mungkin ada banyak kasus di mana ini mungkin berguna.Pemesanan akan selalu tergantung pada implementasi peta spesifik. Menggunakan Java 8 Anda dapat menggunakan salah satu dari ini:
Atau:
Hasilnya akan sama (urutan yang sama). Entri yang didukung oleh peta sehingga Anda mendapatkan urutan yang sama. Yang kedua berguna karena memungkinkan Anda untuk menggunakan lambdas, misalnya jika Anda hanya ingin mencetak objek Integer yang lebih besar dari 5:
Kode di bawah ini menunjukkan iterasi melalui LinkedHashMap dan HashMap normal (contoh). Anda akan melihat perbedaan dalam urutan:
sumber
sumber
Gunakan Java 8:
sumber
Anda dapat melakukannya menggunakan obat generik:
sumber
Solusi berulang efektif atas Peta adalah loop 'untuk setiap' dari Java 5 hingga Java 7. Ini dia:
Dari Java 8 Anda dapat menggunakan ekspresi lambda untuk beralih di atas Peta. Ini adalah 'forEach' yang disempurnakan
sumber
sumber
Ada banyak cara untuk melakukan ini. Di bawah ini adalah beberapa langkah sederhana:
Misalkan Anda memiliki satu Peta seperti:
Kemudian Anda dapat melakukan sesuatu seperti di bawah ini untuk beralih ke elemen peta.
sumber
sumber