Apakah urutan iterasi melalui std :: map diketahui (dan dijamin oleh standar)?

158

Maksud saya adalah - kita tahu bahwa std::mapelemen diurutkan sesuai dengan kunci. Jadi, katakanlah kuncinya adalah bilangan bulat. Jika saya iterate dari std::map::begin()ke std::map::end()menggunakan for, apakah jaminan standar bahwa saya akan iterate akibatnya melalui unsur-unsur dengan kunci, diurutkan dalam urutan menaik?


Contoh:

std::map<int, int> map_;
map_[1] = 2;
map_[2] = 3;
map_[3] = 4;
for( std::map<int, int>::iterator iter = map_.begin();
     iter != map_.end();
     ++iter )
{
    std::cout << iter->second;
}

Apakah ini dijamin untuk dicetak 234atau apakah penerapannya ditentukan?


Alasan kehidupan nyata: Saya memiliki std::mapdengan intkunci. Dalam situasi yang sangat langka, saya ingin beralih melalui semua elemen, dengan kunci, lebih besar dari intnilai konkret . Ya, sepertinya ini std::vectorakan menjadi pilihan yang lebih baik, tetapi perhatikan "situasi yang sangat langka" saya.


EDIT : Saya tahu, bahwa elemen std::mapdiurutkan .. tidak perlu menunjukkannya (untuk sebagian besar jawaban di sini). Saya bahkan menulisnya di pertanyaan saya.
Saya bertanya tentang iterator dan pesanan ketika saya beralih melalui wadah. Terima kasih @ Gerrek SB untuk jawabannya.

Kiril Kirov
sumber
6
Jika Anda tidak tahu: dalam penggunaan kehidupan nyata Anda, Anda dapat menggunakan map::upper_bounduntuk menemukan titik untuk memulai iterasi.
Steve Jessop
Saya tahu ini dan saya tahu tempat yang tepat saya akan mulai iterasi. Saya hanya berkeliaran jika pesanan dijamin.
Kiril Kirov
Vektor jarang tidak masuk akal jika kunci Anda (indeks numerik) sangat bervariasi di seluruh papan. Saya menggunakan solusi serupa yang indeks numeriknya mewakili koordinat y Cartesian dalam ruang 3 dimensi. Menggunakan vektor dalam skenario ini akan meningkatkan jejak memori saya dengan gigabytes. Jadi saya tidak berpikir vektor adalah obat mujarab di sini, jauh dari itu.
Jonathan Neufeld
Saya tidak mengerti pertanyaannya, dan saya akan menjelaskan mengapa melalui eksperimen pikiran. Jika Anda sudah tahu bahwa elemen dipesan, bagaimana mungkin iterasi tidak? Apa artinya memesan, jika tidak berlaku untuk iterasi? Kasus lain apa yang ada di mana pesanan penting, dapat dideteksi, dll.? (Jawabannya diberikan oleh Konstantin .)
underscore_d

Jawaban:

176

Ya, itu dijamin. Selain itu, *begin()memberi Anda elemen terkecil dan *rbegin()terbesar, seperti yang ditentukan oleh operator perbandingan, dan dua nilai kunci adan buntuk mana ekspresi !compare(a,b) && !compare(b,a)itu benar dianggap sama. Fungsi perbandingan default adalah std::less<K>.

Pemesanan bukanlah fitur bonus keberuntungan, melainkan merupakan aspek mendasar dari struktur data, karena pemesanan digunakan untuk menentukan kapan dua kunci adalah sama (dengan aturan di atas) dan untuk melakukan pencarian yang efisien (pada dasarnya biner pencarian, yang memiliki kompleksitas logaritmik dalam jumlah elemen).

Kerrek SB
sumber
std :: map diimplementasikan menggunakan pohon biner, jadi secara teknis tidak ada pencarian biner yang dilakukan.
jupp0r
11
@ jupp0r: Penataan rentang sebagai pohon pencarian biner adalah metode tertentu untuk menerapkan pencarian biner melalui rentang. "Pencarian biner" adalah konsep abstrak dan bukan implementasi tertentu. Tidak masalah apakah Anda melompati offset ke dalam array atau mengikuti node tautan; itu hanya cara khusus "membagi dua kisaran".
Kerrek SB
1
Saya tahu ini adalah posting lama, tetapi untuk menjadi jelas "pencarian efisien" adalah relatif. Secara teknis std::unordered_mapmemiliki waktu pencarian O (1) yang lebih efisien. Keuntungannya std::mapadalah dalam pemesanan kunci, tetapi tidak mencari.
Adam Johnston
42

Ini dijamin oleh persyaratan wadah asosiatif dalam standar C ++. Misalnya lihat 23.2.4 / 10 dalam C ++ 11:

Properti mendasar dari iterator wadah asosiatif adalah milik mereka
beralih melalui wadah dalam urutan kunci yang tidak menurun di mana
non-descending didefinisikan oleh perbandingan yang digunakan untuk membangunnya.
Untuk setiap dua iterator dereference i dan j sedemikian rupa sehingga jarak dari i ke j adalah
positif,
  value_comp (* j, * i) == false

dan 23.2.4 / 11

Untuk wadah asosiatif dengan kunci unik yang berlaku kuat,
  value_comp (* i, * j)! = false.
Konstantin Oznobihin
sumber
32

Saya pikir ada kebingungan dalam struktur data.

Dalam sebagian besar bahasa, a maphanyalah sebuah AssociativeContainer: a memetakan kunci nilai. Dalam bahasa "yang lebih baru", ini umumnya dicapai menggunakan peta hash, sehingga tidak ada pesanan yang dijamin.

Namun dalam C ++, ini tidak begitu:

  • std::mapadalah wadah asosiatif yang diurutkan
  • std::unordered_map adalah wadah asosiatif berbasis tabel hash yang diperkenalkan di C ++ 11

Jadi, untuk memperjelas jaminan pemesanan.

Di C ++ 03:

  • std::set, std::multiset, std::mapDan std::multimapdijamin akan dipesan sesuai tombol (dan kriteria yang disediakan)
  • dalam std::multisetdan std::multimap, standar tidak memaksakan jaminan pesanan pada elemen yang setara (yaitu, yang membandingkan sama)

Dalam C ++ 11:

  • std::set, std::multiset, std::mapDan std::multimapdijamin akan dipesan sesuai tombol (dan kriteria yang disediakan)
  • dalam std::multisetdan std::multimap, Standar memaksakan bahwa elemen yang setara (elemen yang sebanding) dipesan sesuai dengan urutan penyisipannya (pertama kali disisipkan terlebih dahulu)
  • std::unordered_*kontainer, seperti namanya, tidak dipesan. Terutama, urutan elemen dapat berubah ketika wadah dimodifikasi (setelah penyisipan / penghapusan).

Ketika Standar mengatakan bahwa elemen dipesan dengan cara tertentu, itu berarti:

  • saat iterasi, Anda melihat elemen dalam urutan yang ditentukan
  • ketika iterasi terbalik, Anda melihat elemen dalam urutan yang berlawanan

Saya harap ini membersihkan semua kebingungan.

Matthieu M.
sumber
Ini tidak ada hubungannya dengan pertanyaan saya, maaf :) Saya tahu mana yang dipesan, dan yang mana - tidak. Dan saya meminta pesanan ketika saya mengulangi elemen.
Kiril Kirov
10
@ KirilKirov: yah, definisi dari wadah asosiatif yang dipesan adalah ketika iterasi melalui itu elemen-elemen dipesan.
Matthieu M.
Yah, saya kira Anda benar, tetapi saya tidak tahu itu dan itulah yang saya tanyakan :)
Kiril Kirov
4

Apakah ini dijamin untuk mencetak 234 atau implementasi sudah ditentukan?

Ya, std::mapadalah wadah yang diurutkan, dipesan oleh Keybersama dengan yang disediakan Comparator. Jadi dijamin.

Saya ingin beralih melalui semua elemen, dengan kunci, lebih besar dari nilai int konkret.

Itu pasti mungkin.

jpalecek
sumber
3

Ya ... elemen dalam a std::mapmemiliki urutan lemah yang ketat, artinya elemen akan terdiri dari satu set (yaitu, tidak akan ada pengulangan kunci yang "sama"), dan kesetaraan ditentukan dengan menguji pada setiap dua kunci A dan B, bahwa jika kunci A tidak kurang dari kunci B, dan B tidak kurang dari A, maka kunci A sama dengan kunci B.

Yang sedang berkata, Anda tidak bisa mengurutkan elemen-elemen dari a std::mapjika urutan lemah untuk tipe itu ambigu (dalam kasus Anda, di mana Anda menggunakan integer sebagai tipe-kunci, itu bukan masalah). Anda harus dapat mendefinisikan operasi yang mendefinisikan urutan total pada jenis yang Anda gunakan untuk kunci di Anda std::map, jika tidak, Anda hanya akan memiliki pesanan parsial untuk elemen Anda, atau poset, yang memiliki properti di mana A mungkin tidak dapat dibandingkan dengan B. Apa yang biasanya akan terjadi dalam skenario ini adalah bahwa Anda akan dapat memasukkan pasangan kunci / nilai, tetapi Anda mungkin berakhir dengan pasangan kunci / nilai duplikat jika Anda mengulangi seluruh peta, dan / atau mendeteksi "hilang" pasangan kunci / nilai ketika Anda mencoba melakukan std::map::find()pasangan kunci / nilai tertentu di peta.

Jason
sumber
Sebagai jawaban lain, ini sebenarnya tidak menjawab pertanyaan saya, terima kasih.
Kiril Kirov
-3

begin () dapat memberikan elemen terkecil. Tetapi implementasi tergantung. Apakah ini ditentukan dalam standar C ++? Jika tidak, maka berbahaya untuk membuat asumsi ini.

pyang
sumber