std::set menjamin urutan dan pencarian O(log n). std::unordered_set menawarkan tawar-menawar yang berbeda: buang urutan, dapat kecepatan. Dengan menyimpan elemen berdasarkan nilai hash-nya, pencarian, penyisipan, dan penghapusan berlangsung O(1) rata-rata — langsung ke “laci” yang tepat tanpa menelusuri pohon. Konsekuensinya harus dipahami sejak awal: urutan iterasi unordered_set tidak ditentukan standar dan bisa berbeda antar compiler, antar versi pustaka, bahkan antar program; iterasi dua kali pada program yang sama memang memberi urutan yang sama, tetapi itu detail implementasi, bukan janji. Chapter ini memperagakan hash set, menunjukkan cara aman mencetak isinya secara deterministik, dan membandingkannya dengan std::set.
Cara Kerja Hash dalam Satu Paragraf #
std::unordered_set menghitung angka hash dari setiap elemen lewat std::hash, lalu angka itu dimodulo jumlah bucket — laci-laci penyimpanan internal — untuk memilih laci tempat elemen tinggal. Mencari "cache" berarti menghitung hash-nya, melompat langsung ke laci yang sesuai, dan membandingkan sedikit kandidat saja. Rasio size / bucket_count disebut load factor; bila ia melewati batas tertentu, container melakukan rehash (membuat bucket baru dan memindahkan semua elemen), yang inilah sebabnya operasi O(1) hanya berlaku rata-rata — kadang-kadang satu operasi lebih berat dari biasanya.
Operasi Inti unordered_set #
Program berikut menguji keanggotaan, pencetakan terurut, data internal, dan penghapusan.
#include <algorithm> // std::sort: mengurutkan salinan untuk output deterministik
#include <iostream> // std::cout
#include <string> // std::string
#include <unordered_set> // std::unordered_set
#include <vector> // std::vector sebagai wadah salinan terurut
int main() {
std::unordered_set<std::string> kata;
kata.insert("thread");
kata.insert("cache");
kata.insert("system");
kata.insert("cache"); // duplikat: tidak berpengaruh
std::cout << "ukuran : " << kata.size() << "\n";
std::cout << "cache ada? : " << (kata.contains("cache") ? "ya" : "tidak") << "\n";
std::cout << "mutex ada? : " << (kata.contains("mutex") ? "ya" : "tidak") << "\n";
// URUTAN ITERASI unordered_set TIDAK DITENTUKKAN standar.
// Agar output deterministik: salin ke vector, urutkan, baru cetak.
std::vector<std::string> salinan(kata.begin(), kata.end());
std::sort(salinan.begin(), salinan.end());
std::cout << "isi terurut :";
for (const std::string& k : salinan) std::cout << ' ' << k;
std::cout << "\n";
// data internal hash: jumlah bucket dan beban rata-ratanya
// (angka tetap untuk alur yang sama, tetapi bisa berbeda antar standard library)
std::cout << "bucket_count : " << kata.bucket_count() << "\n";
std::cout << "load_factor : " << kata.load_factor() << "\n";
// erase lewat iterator find, bukan menyalin isi
kata.erase(kata.find("system"));
std::cout << "system ada? : " << (kata.contains("system") ? "ya" : "tidak") << "\n";
return 0;
}Output yang Dihasilkan:
ukuran : 3
cache ada? : ya
mutex ada? : tidak
isi terurut : cache system thread
bucket_count : 13
load_factor : 0.230769
system ada? : tidakMari kita bedah:
- Baris 9-12 (menambah elemen): empat kali
insert, tetapisizehanya3— duplikat"cache"ditolak diam-diam, sama seperti padastd::set. - Baris 15-16 (
contains): fungsicontains(masuk standar sejak C++20) adalah cara paling terbaca menanyakan keberadaan. Di balik layar ia menghitung hash kunci dan melompat ke bucket-nya — inilah lompatan O(1) rata-rata yang menjadi alasan utama memakai unordered container. - Baris 20-24 (pola cetak deterministik): karena urutan iterasi hash set tidak dijamin, kita menyalin isinya ke
std::vector, mengurutkan denganstd::sort, lalu mencetak salinannya. Jadilah outputcache system threadyang sama di mesin mana pun. Pola ini wajib ketika isi unordered container ikut ditampilkan, disimpan, atau dibandingkan. - Baris 28-29 (data internal):
bucket_countmenunjukkan ada 13 laci untuk 3 elemen, danload_factorkarenanya 0.230769. Angka ini deterministik untuk alur program yang sama pada standard library yang sama, tetapi bisa berbeda antar implementasi (libstdc++, libc++, MSVC) — jadikan hanya referensi mental, bukan kontrak. - Baris 32 (
erase): menghapus lewatfindmengembalikan iterator ke elemen;erasemenerima iterator itu dan melepas elemennya. Versi satu argumen nilai (kata.erase("system")) juga sah pada unordered_set.
Set vs unordered_set: Menimbang Keduanya #
Keduanya menyimpan nilai unik; pembedanya ada pada janji urutan dan profil kecepatan.
| Aspek | std::set | std::unordered_set |
|---|---|---|
| Urutan iterasi | terurut menaik, dijamin | tidak ditentukan |
| Pencarian/insert/erase | O(log n) selalu | O(1) rata-rata, O(n) terburuk |
| Rehash tiba-tiba | tidak ada | ada (saat load factor melampaui batas) |
Butuh operator< pada tipe | ya | butuh std::hash (sudah ada untuk tipe bawaan) |
| Iterasi & memori | pohon: pointer per simpul | bucket array + simpul |
| Cocok untuk | output terurut, pencarian rentang | keanggotaan murni, lookup cepat |
Hash set menyelesaikan pertanyaan keanggotaan dengan kilat. Chapter berikutnya melengkapi keluarga hash dengan pasangan kunci-nilai: std::unordered_map, dan aturan deterministik yang sama berlaku penuh di sana.
Rujukan resmi: cppreference — std::unordered_set , cppreference — unordered_set::contains , cppreference — unordered_set::load_factor .