Sebagian besar pertanyaan tentang data bukanlah “berapa nilainya”, melainkan “apakah nilai ini sudah pernah muncul?”. Panitia seminar harus tahu nama mana yang belum terdaftar, gerbang stadion harus tahu tiket mana yang sudah dipakai masuk, dan platform berita harus tahu tag mana yang sudah menempel pada artikel. Semua pertanyaan itu berbicara tentang keunikan, dan koleksi yang lahir untuk menjawabnya adalah HashSet<T> — himpunan nilai yang menolak duplikat dengan otomatis. Chapter ini membahas cara memasukkan dan memeriksa anggota, memadatkan daftar penuh duplikasi, serta operasi himpunan klasik seperti gabungan (union) dan irisan (intersection).
HashSet Menolak Duplikat #
Method insert pada HashSet memiliki satu perilaku khas yang tidak dimiliki HashMap: nilai kembaliannya adalah bool yang melaporkan apakah nilai yang dikirim benar-benar baru. Program berikut mengelola daftar hadir rapat dan memanfaatkan laporan tersebut:
use std::collections::HashSet;
fn main() {
let mut attendees = HashSet::new();
let first_add = attendees.insert("Budi");
let second_add = attendees.insert("Budi");
attendees.insert("Siti");
attendees.insert("Andi");
println!("Penambahan 'Budi' pertama: {}", first_add);
println!("Penambahan 'Budi' kedua : {}", second_add);
println!("Jumlah peserta unik: {}", attendees.len());
println!("Apakah 'Siti' hadir? {}", attendees.contains("Siti"));
println!("Apakah 'Rina' hadir? {}", attendees.contains("Rina"));
let removed = attendees.remove("Andi");
println!("Menghapus 'Andi' berhasil: {}", removed);
println!("Jumlah peserta akhir: {}", attendees.len());
}Output yang Dihasilkan:
Penambahan 'Budi' pertama: true
Penambahan 'Budi' kedua : false
Jumlah peserta unik: 3
Apakah 'Siti' hadir? true
Apakah 'Rina' hadir? false
Menghapus 'Andi' berhasil: true
Jumlah peserta akhir: 2Mari kita bedah kode di atas baris per baris:
- Baris 4 (
let mut attendees = HashSet::new();): himpunan kosong bertipeHashSet<&str>— tipe elemennya disimpulkan dari isi pertama yang masuk. - Baris 6-7 (
let first_add = ...; let second_add = ...;):insertpertama menemukan keadaan kosong, memasukkan"Budi", dan mengembalikantruekarena keanggotaan baru tercipta.insertkedua dengan nilai yang sama mengembalikanfalse— set menolak duplikat dengan diam-diam dan tidak mengubah isinya sama sekali. - Baris 13 (
attendees.len()): meskipun"Budi"dikirim dua kali, jumlah anggota hanya3— satu Budi, satu Siti, satu Andi. - Baris 15-16 (
attendees.contains(...)): methodcontainsmemeriksa keanggotaan dan mengembalikanbool."Rina"tidak pernah dimasukkan, jadi jawabannyafalse. - Baris 18-19 (
attendees.remove("Andi")):removemenghapus anggota dan melaporkantrue; menghapus anggota yang tidak ada akan melaporkanfalse— polaOptiondariHashMapdi sini diringkas menjadiboolkarena nilai yang dihapus tidak dikembalikan.
Merapatkan Daftar yang Berulang #
Sumber data mentah sering datang penuh duplikasi: pemindai tiket mengirim deretan kode, dan tugas kita adalah menghitung tiket valid yang berbeda. Perjalanan dari Vec ke HashSet cukup satu loop insert:
use std::collections::HashSet;
fn main() {
let scanned_tickets = vec!["T-001", "T-002", "T-001", "T-003"];
let mut valid_tickets = HashSet::new();
for ticket in scanned_tickets {
valid_tickets.insert(ticket);
}
println!("Tiket yang discan: 4");
println!("Tiket unik: {}", valid_tickets.len());
}Output yang Dihasilkan:
Tiket yang discan: 4
Tiket unik: 3Empat pemindaian menghasilkan tiga tiket unik — T-001 terpindai dua kali dan set hanya menghitungnya sekali. Mari kita bedah kode di atas baris per baris:
- Baris 4 (
let scanned_tickets = vec![...]): empat hasil pemindaian tersimpan dalamVec<&str>dengan satu duplikasi — kode"T-001"muncul dua kali. - Baris 5 (
let mut valid_tickets = HashSet::new();): himpunan penampung dimulai kosong. - Baris 7-9 (
for ticket in scanned_tickets): loop memindahkan setiap kode tiket dari vector ke set. Insert pertama"T-001"mengembalikantrue, insert keduanya mengembalikanfalse— nilai kembalian dibiarkan karena penolakan duplikat memang perilaku yang kita inginkan. - Baris 12 (
valid_tickets.len()): himpunan melaporkan3anggota unik meskipun sumbernya memuat4entri. Pola ini bekerja untuk sumber duplikasi apa pun — daftar belanja bertuliskan barang yang sama, daftar undangan dengan nama kembar, atau hasil penggabungan banyak file.
Mekanisme: Sebuah HashMap Tanpa Value #
HashSet di dalamnya benar-benar dibangun di atas mesin HashMap: setiap nilai di-hash, hash menentukan bucket penyimpanan, dan keanggotaan diperiksa lewat perbandingan kesetaraan. Bedanya, set hanya menyimpan kunci tanpa value yang menempel — seperti papan skor yang hanya mencatat nama tim tanpa angka. Konsekuensinya menular: pencarian contains selesai dalam waktu rata-rata konstan, nilai yang bisa di-hash dan dibandingkan kesetaraannya bisa menjadi anggota, dan urutan iterasi tidak dijamin karena biji acak hash berbeda di setiap eksekusi — sifat yang sama dengan HashMap.
Urutan acak tersebut tidak mengganggu contoh-contoh di atas karena insert, contains, dan len semuanya bekerja pada satu nilai. Begitu kita mencetak seluruh anggota atau menggabungkan dua set, kita kembali memakai resep “kumpulkan lalu sort” dari chapter HashMap: Iterasi dan Update.
Operasi Himpunan: Intersection, Union, dan Difference #
Kekuatan sesungguhnya HashSet muncul ketika dua himpunan bertemu. Ekstrakurikuler futsal punya anggota sendiri, ekstrakurikuler basket punya anggota sendiri, dan keputusan penjadwalan butuh tahu siapa yang terjebak di dua kegiatan sekaligus:
use std::collections::HashSet;
fn main() {
let mut futsal_club = HashSet::new();
futsal_club.insert("Andi");
futsal_club.insert("Budi");
futsal_club.insert("Citra");
let mut basket_club = HashSet::new();
basket_club.insert("Budi");
basket_club.insert("Citra");
basket_club.insert("Dewi");
let mut both_clubs = Vec::new();
for student in futsal_club.intersection(&basket_club) {
both_clubs.push(student);
}
both_clubs.sort();
println!("Mengikuti dua klub: {:?}", both_clubs);
let mut all_students = Vec::new();
for student in futsal_club.union(&basket_club) {
all_students.push(student);
}
all_students.sort();
println!("Semua siswa unik: {:?}", all_students);
let mut futsal_only = Vec::new();
for student in futsal_club.difference(&basket_club) {
futsal_only.push(student);
}
futsal_only.sort();
println!("Hanya futsal: {:?}", futsal_only);
let mut core_members = HashSet::new();
core_members.insert("Budi");
println!("Inti futsal bagian dari klub: {}", core_members.is_subset(&futsal_club));
}Output yang Dihasilkan:
Mengikuti dua klub: ["Budi", "Citra"]
Semua siswa unik: ["Andi", "Budi", "Citra", "Dewi"]
Hanya futsal: ["Andi"]
Inti futsal bagian dari klub: trueOutput stabil karena setiap operasi hasilnya ditampung ke Vec lalu di-sort sebelum dicetak. Mari kita bedah logikanya:
- Baris 14-19 (
intersection(&basket_club)): intersection menghasilkan penelusuran anggota-anggota yang hadir di kedua himpunan — Budi dan Citra satu-satunya nama yang muncul di dua daftar klub. Loopformenelusuri hasil intersection dan memindahkan setiap anggota keboth_clubs, lalusortmemberi keterurutan abjad. - Baris 21-26 (
union(&basket_club)): union menggabungkan keanggotaan kedua himpunan tanpa duplikasi — empat nama unik meski Budi dan Citra tercatat dua kali di sumbernya. - Baris 28-33 (
difference(&basket_club)): difference menghasilkan anggota futsal yang tidak ada di basket — hanya Andi. Arahnya penting:basket_club.difference(&futsal_club)akan menghasilkan Dewi. - Baris 35-37 (
is_subset(&futsal_club)): method relasiis_subsetmenjawab pertanyaan “apakah setiap anggota himpunan pertama juga anggota himpunan kedua” —core_membersberanggotakan Budi yang memang anggota futsal, jadi jawabannyatrue.
Keluarga method relasi lainnya mengikuti logika yang sama: is_superset membalik arah pertanyaan, dan is_disjoint memeriksa dua himpunan yang tidak memiliki anggota bersama sama sekali.
Ringkasan #
HashSet menyimpan himpunan nilai unik: insert menolak duplikat dan melaporkan kebaruan lewat bool, contains memeriksa keanggotaan, dan remove menghapus dengan laporan berhasil atau tidak. Mekanisme internalnya sama dengan HashMap — hash menentukan bucket, waktu pencarian rata-rata konstan, dan urutan iterasi acak — sehingga output terurut memerlukan pola kumpulkan-lalu-sort. Operasi himpunan intersection, union, dan difference menjawab pertanyaan silang antar dua kumpulan data, ditemani method relasi seperti is_subset. Berikutnya kita bertemu koleksi yang mengambil jalan sepenuhnya berbeda dari hash: BTreeMap dan keterurutan berbasis pohon.