Sepanjang section ini kita mengenal empat koleksi standar Rust: Vec untuk daftar berindex, HashMap untuk pasangan key-value, HashSet untuk himpunan unik, dan BTreeMap untuk key-value yang terurut. Kita menguji masing-masing dengan data belanja, skor pertandingan, stok toko, daftar hadir, dan nilai ujian. Pilihan koleksi yang tepat menentukan tiga hal sekaligus: kecepatan operasi utama, kebenaran kode (berapa banyak cabang None yang harus kita tangani), dan kejelasan niat program bagi pembaca berikutnya. Chapter penutup ini melatih refleks pemilihan — dengan satu program kecil yang menggabungkan keempat koleksi, lalu peta keputusan untuk memilih di antara mereka, termasuk koleksi tambahan yang menunggu di std::collections.

Empat Koleksi, Empat Pola Akses #

Pemilihan koleksi sebaiknya dimulai dari satu pertanyaan: bagaimana data ini diakses? Empat koleksi kita menjawab empat pola akses yang berbeda:

KoleksiPola akses yang dijawabOperasi khas
Vec<T>“elemen ke-N” — daftar dengan urutan dan posisipush, insert, remove, [index]
HashMap<K, V>“value untuk key ini” — pencarian tunggal tercepatinsert, get, entry().or_insert()
HashSet<T>“apakah nilai ini sudah ada?” — keunikaninsert, contains, intersection
BTreeMap<K, V>“key di rentang ini, secara terurut” — keterurutanrange, first_key_value, iterasi terurut

Baris pertama tabel memuat aturan paling sering dilupakan pemula: Vec bukan sekadar “wadah banyak data”, melainkan wadah data yang posisinya bermakna — item pertama pesanan, urutan tampilan menu, baris ke-N pada laporan. Ketika posisi tidak bermakna dan yang kita cari adalah pasangan key-value, memaksakan Vec berarti menulis pencarian manual for berikut cabang None-nya — pekerjaan yang sudah dikerjakan HashMap dengan get.

Contoh Kasus: Laporan Penjualan Toko Buku #

Saatnya keempat koleksi bekerja dalam satu program. Kasusnya: toko buku online menerima deretan pesanan, lalu mencetak rekap pendapatan per judul secara terurut:

rust
use std::collections::{BTreeMap, HashMap, HashSet};

fn main() {
    // Vec: urutan pesanan pelanggan harus terjaga
    let orders = vec![
        String::from("Laskar Pelangi"),
        String::from("Bumi Manusia"),
        String::from("Laskar Pelangi"),
    ];

    // HashMap: pencarian harga per judul dengan key String
    let mut prices = HashMap::new();
    prices.insert(String::from("Bumi Manusia"), 105_000);
    prices.insert(String::from("Laskar Pelangi"), 95_000);

    // HashSet: mencatat judul unik yang dipesan
    let mut unique_titles = HashSet::new();

    // BTreeMap: rekap pendapatan, siap dicetak terurut
    let mut revenue = BTreeMap::new();

    for order in orders {
        unique_titles.insert(order.clone());

        let price = match prices.get(&order) {
            Some(price) => *price,
            None => 0,
        };

        let total = revenue.entry(order).or_insert(0);
        *total += price;
    }

    println!("Judul unik yang dipesan: {}", unique_titles.len());
    println!("Pendapatan per judul (terurut):");
    for (title, total) in &revenue {
        println!("- {}: Rp{}", title, total);
    }
}

Output yang Dihasilkan:

text
Judul unik yang dipesan: 2
Pendapatan per judul (terurut):
- Bumi Manusia: Rp105000
- Laskar Pelangi: Rp190000

Mari kita bedah peran setiap koleksi di dalamnya:

  1. Baris 5-9 (let orders = vec![...]): Vec<String> menyimpan urutan kedatangan pesanan. Pesanan ketiga memang duplikat pesanan pertama — vector membiarkannya, karena tugas vector di sini hanya menjaga urutan masuk.
  2. Baris 12-14 (let mut prices = HashMap::new(); ...): HashMap<String, i32> memegang daftar harga. Pola aksesnya selalu “harga untuk judul ini” — tak pernah iterasi seluruhnya, tak pernah butuh urutan — persis wilayah kerja HashMap.
  3. Baris 17 (let mut unique_titles = HashSet::new();): HashSet<String> menghitung berapa judul berbeda yang dipesan hari itu. Duplikasi “Laskar Pelangi” lenyap otomatis saat insert di baris 23.
  4. Baris 20 (let mut revenue = BTreeMap::new();): rekap pendapatan harus tampil terurut abjad di laporan akhir, jadi wadahnya BTreeMap sejak awal — bukan HashMap yang disortir di akhir.
  5. Baris 25-28 (match prices.get(&order)): pencarian harga mengikuti pola Option — Some(price) => *price mengambil harga, dan None => 0 menjawab skenario judul yang tidak punya harga terdaftar (misalnya buku gratis).
  6. Baris 30-31 (revenue.entry(order).or_insert(0)): akumulasi pendapatan memakai Entry API dari chapter sebelumnya — judul pertama memesan lahir dari 0, pesanan lanjutan menambah total yang sudah ada.
  7. Baris 36-38 (for (title, total) in &revenue): iterasi BTreeMap mencetak hasil terurut tanpa proses sortir tambahan. “Laskar Pelangi” muncul dengan total Rp190000 karena dua pesanan 95_000 terakumulasi di satu key.

Perhatikan bahwa tidak ada satu pun baris pencarian manual: tidak ada for yang menyisir vector untuk mencari harga, tidak ada pengecekan duplikasi yang ditulis tangan. Setiap kebutuhan jatuh ke koleksi yang memang dirancang untuk kebutuhan tersebut — itulah makna “memilih koleksi” dalam praktik.

Koleksi Lain di std::collections #

Empat koleksi utama bukan seluruh isi std::collections. Tiga anggota lain layak dikenali sejak sekarang:

  • VecDeque<T> — vector dua ujung: penyisipan dan penghapusan efisien di depan maupun belakang. Pilihan tepat untuk antrean sungguhan (antrian pesanan masuk dari belakang, diproses dari depan) yang pada Vec terpaksa memakai remove(0) yang menggeser seluruh isi.
  • BinaryHeap<T> — tumpukan prioritas: pop selalu mengembalikan nilai terbesar. Pilihan tepat untuk tugas berprioritas, misalnya pesanan express diproses sebelum pesanan reguler.
  • BTreeSet<T> — padanan HashSet dengan keterurutan B-tree, untuk kebutuhan keunikan sekaligus laporan terurut.

Modul yang sama juga menyimpan LinkedList, namun kebutuhan yang benar-benar menuntut linked list sangat langka di praktik. Aturan pragmatis Rust berbunyi: mulai dari Vec atau HashMap, ganti hanya ketika profil akses data membuktikan kebutuhan lain. Koleksi tambahan ini tidak kita bahas mendalam di section dasar; cukup tahu mereka menunggu di lemari yang sama.

Daftar Periksa Sebelum Memilih #

Empat pertanyaan berikut memetakan hampir semua keputusan pemilihan koleksi:

  1. Apa pola akses dominannya? Posisi berindex → Vec. Key menuju value → HashMap atau BTreeMap. Keunikan → HashSet atau BTreeSet.
  2. Apakah urutan penting? Tidak penting → versi hash, yang lebih cepat untuk lookup tunggal. Penting untuk tampilan, rentang, atau min/max → versi B-tree.
  3. Apakah duplikat sah? Sah → Vec atau map (key unik, value bebas). Tidak sah → set.
  4. Apakah ukuran diketahui di muka? Ya → Vec::with_capacity(n) menghemat realokasi.

Ringkasan #

Pilihan koleksi adalah keputusan arsitektur kecil yang berdampak besar: Vec untuk daftar yang posisinya bermakna, HashMap untuk pencarian key-value tercepat, HashSet untuk keunikan, dan BTreeMap untuk keterurutan dan rentang. Program laporan toko buku menunjukkan keempatnya bekerja berdampingan — setiap kebutuhan dijawab koleksi yang memang dirancang untuknya, tanpa pencarian manual ataupun sortir tambahan. Ketika profil akses melampaui keempat pola itu, std::collections masih menyimpan VecDeque, BinaryHeap, dan BTreeSet. Dengan koleksi terkuasai, perjalanan kita melanjutkan ke realitas yang tak terhindarkan: program yang gagal — dan bagaimana Rust menanganinya lewat Result di section Penanganan Error.