Banyak data di dunia nyata berbentuk pasangan: nama barang dan stoknya, kata dan frekuensinya, ID dan nama pemiliknya. std::map<K, V> menyimpan pasangan itu dengan aturan yang sama dengan std::set — kunci unik dan selalu terurut — namun setiap kini kunci membawa satu nilai teman. Karena dibangun di atas pohon yang sama, pencarian, penyisipan, dan penghapusan bernilai O(log n) menurut jumlah entri. Pada Chapter ini kita menimbang tiga cara menyentuh isi map (operator[], at(), insert), membaca isinya dengan structured bindings, lalu melihat std::multimap yang memperbolehkan satu kunci memakai banyak nilai.

Tiga Cara Menyentuh Isi Map #

Cara ketiganya terlihat mirip, tetapi semantiknya berbeda dan salah pilih bisa berujung bug halus. Program berikut menunjukkan ketiganya berdampingan.

cpp
#include <iostream>  // std::cout
#include <map>       // std::map dan std::multimap
#include <string>    // std::string

int main() {
    // --- map: pasangan kunci -> nilai, terurut menurut kunci ---
    std::map<std::string, int> stok;
    stok["kopi"] = 12;    // operator[]: buat bila kunci belum ada
    stok["teh"]  = 30;
    stok["gula"] = 7;

    stok["kopi"] -= 5;    // akses lalu ubah nilainya
    std::cout << "kopi tersisa: " << stok["kopi"] << "\n";

    // HATI-HATI: membaca kunci yang belum ada MEMBUAT entri baru bernilai 0
    std::cout << "cokelat (baru): " << stok["cokelat"] << "\n";
    std::cout << "ukuran sekarang: " << stok.size() << "\n";

    // at() menanyakan nilai tanpa membuat entri; melempar exception bila absen
    std::cout << "teh via at(): " << stok.at("teh") << "\n";

    // iterasi map: elemen berupa pair (kunci, nilai), selalu terurut menurut kunci
    std::cout << "Daftar stok (terurut):\n";
    for (const auto& [nama, jumlah] : stok) {
        std::cout << "  " << nama << " : " << jumlah << "\n";
    }

    // insert TIDAK menimpa bila kunci sudah ada
    auto hasil = stok.insert({"teh", 99});
    std::cout << "insert teh=99 berhasil: "
              << (hasil.second ? "ya" : "tidak") << "\n";
    std::cout << "teh masih: " << stok.at("teh") << "\n";

    // --- multimap: satu kunci boleh membawa banyak nilai ---
    std::multimap<std::string, std::string> kontak;
    kontak.insert({"budi", "[email protected]"});
    kontak.insert({"budi", "[email protected]"});
    kontak.insert({"siti", "[email protected]"});

    std::cout << "Email budi (" << kontak.count("budi") << " entri):\n";
    for (const auto& [nama, email] : kontak) {
        if (nama == "budi") std::cout << "  " << email << "\n";
    }

    return 0;
}

Output yang Dihasilkan:

text
kopi tersisa: 7
cokelat (baru): 0
ukuran sekarang: 4
teh via at(): 30
Daftar stok (terurut):
  cokelat : 0
  gula : 7
  kopi : 7
  teh : 30
insert teh=99 berhasil: tidak
teh masih: 30
Email budi (2 entri):
  [email protected]
  [email protected]

Mari kita bedah:

  1. Baris 8-10 (operator[]): penugasan lewat kurung siku membuat entri bila kunci belum ada — cara paling ringkas menambah data. stok["kopi"] -= 5 kemudian membaca dan mengubah nilai yang sudah ada.
  2. Baris 16 (jebakan operator[]): hanya membaca stok["cokelat"] ternyata membuat entri baru bernilai 0! operator[] tidak bisa membedakan maksud membaca dari menulis, sehingga size() naik menjadi 4 — inilah bug halus yang paling sering dijumpai pemula.
  3. Baris 20 (at()): at() tidak pernah membuat entri. Bila kunci tidak ada, ia melempar exception std::out_of_range (kita dalami di Section Error Handling) — jauh lebih aman untuk membaca ketika keberadaan kunci belum pasti.
  4. Baris 24-26 (iterasi + structured bindings): setiap elemen map adalah std::pair berisi first (kunci) dan second (nilai). Structured bindings yang kita pelajari pada Chapter Structured Bindings membongkarnya menjadi nama dan jumlah, dan urutan keluar otomatis terurut: cokelat, gula, kopi, teh.
  5. Baris 29-32 (insert): berbeda dari operator[], insert tidak menimpa bila kunci sudah ada. Kembalian pair<iterator, bool> melapor berhasil=tidak, dan nilai teh tetap 30.
  6. Baris 35-43 (multimap): kunci "budi" membawa dua email sekaligus. Karena multimap tidak punya operator[], penambahannya lewat insert, dan count("budi") menghitung berapa banyak nilai yang menempel pada kunci itu.

Memilih antara operator[], at(), dan insert #

Tabel ini merangkum semantik ketiganya supaya keputusan kita cepat dan konsisten.

CaraKunci belum adaKunci sudah adaCocok untuk
m[k] = vmembuat entrimenimpa nilaimenambah/mengubah dengan sadar
m.at(k)melempar exceptionmembaca sajamembaca dengan aman
m.insert({k, v})menambahtidak menimpamenambah tanpa merusak
m[k] (baca)membuat entri 0!membacahindari untuk baca

Satu pola umum yang sehat: gunakan m[k]++ untuk menghitung frekuensi (karena kita memang ingin entri baru mulai dari 0), gunakan at() untuk membaca, dan insert/emplace untuk mengisi data awal.

std::map memberi kita urutan kunci dan O(log n) untuk semua operasi. Chapter berikutnya membuka kandidat yang menukar urutan dengan kecepatan rata-rata O(1): std::unordered_set, pertama dari keluarga hash container.

Rujukan resmi: cppreference — std::map , cppreference — map::operator[] , cppreference — std::multimap .