HashMap — Rust

HashMap di Rust HashMap menyimpan data key-value pairs, mirip object di JavaScript atau dictionary di Python. Import & Membuat HashMap use…

HashMap<K, V> menyimpan pasangan kunci (key, tipe K) dan nilai (value, tipe V): kamu mencari nilai lewat kuncinya, bukan lewat nomor urut seperti di Vec. Bayangin daftar harga di dinding kasir Kedai Martabak Bang Rusdi. Kasir mencari "es teh" dan ketemu 5000, tanpa peduli es teh ada di baris ke berapa. Di bawahnya ada kertas turus untuk menghitung pesanan per menu: tiap ada pesanan, cari baris menunya lalu tambah satu coretan, dan menu yang baru pertama kali dipesan dapat baris baru yang mulai dari 0. Nama menu itu kunci, harga atau jumlah coretan itu nilai.

🧪 Tebakan cepat: tiga menu dimasukkan berurutan, martabak manis, martabak telur, lalu es teh, kemudian dicetak dengan for (menu, harga) in &harga_menu. Dalam urutan apa barisnya tercetak? Tebak dulu, jawabannya ada di bagian "Urutannya gak bisa diandalkan".

Membuat HashMap, insert, dan get

use std::collections::HashMap;

fn main() {
    let mut harga_menu: HashMap<String, u32> = HashMap::new();
    harga_menu.insert(String::from("martabak manis"), 32000);
    harga_menu.insert(String::from("martabak telur"), 38000);
    harga_menu.insert(String::from("es teh"), 5000);
    println!("Jumlah menu: {}", harga_menu.len());
    println!("{:?}", harga_menu.get("es teh"));
    println!("{:?}", harga_menu.get("kopi"));
    match harga_menu.get("martabak telur") {
        Some(harga) => println!("Martabak telur: {}", harga),
        None => println!("Gak ada di menu"),
    }
    let lama = harga_menu.insert(String::from("es teh"), 6000);
    println!("{:?} {}", lama, harga_menu.len());
}
Jumlah menu: 3
Some(5000)
None
Martabak telur: 38000
Some(5000) 3

Menghitung pesanan dengan entry().or_insert()

Ini kertas turusnya. Enam pesanan masuk, dan tiap menu dihitung:

use std::collections::HashMap;

fn main() {
    let pesanan = ["martabak manis", "es teh", "martabak manis", "martabak telur", "es teh", "martabak manis"];
    let mut jumlah: HashMap<&str, u32> = HashMap::new();
    for menu in pesanan {
        *jumlah.entry(menu).or_insert(0) += 1;
    }
    println!("martabak manis: {}", jumlah["martabak manis"]);
    println!("es teh: {}", jumlah["es teh"]);
    println!("martabak telur: {}", jumlah["martabak telur"]);
}
martabak manis: 3
es teh: 2
martabak telur: 1
Pesanan Baris menunya Isi jumlah sesudahnya
martabak manis belum ada: dibuat 0, lalu +1 manis 1
es teh belum ada: dibuat 0, lalu +1 manis 1, es teh 1
martabak manis sudah ada: +1 manis 2, es teh 1
martabak telur belum ada: dibuat 0, lalu +1 manis 2, es teh 1, telur 1
es teh sudah ada: +1 manis 2, es teh 2, telur 1
martabak manis sudah ada: +1 manis 3, es teh 2, telur 1

Urutannya gak bisa diandalkan

Ini program tebakan tadi, cargo build sekali lalu dijalankan dua kali:

$ ./target/debug/kedai
martabak manis: 32000
es teh: 5000
martabak telur: 38000
$ ./target/debug/kedai
es teh: 5000
martabak telur: 38000
martabak manis: 32000

Program yang sama, urutan yang beda, dan dua-duanya bukan urutan saat insert. Itu jawaban tebakannya: urutannya gak bisa ditebak. HashMap menaruh tiap pasangan di laci yang dihitung dari kuncinya (namanya hash). Supaya orang gak bisa sengaja mengirim kunci yang menumpuk di laci yang sama dan memperlambat program, Rust mengacak rumus hash itu tiap kali program jalan (RandomState). Akibatnya urutan laci, dan urutan for, bisa berubah tiap kali jalan.

BTreeMap kalau urutan penting

BTreeMap dipakai persis seperti HashMap, tapi selalu urut menurut kuncinya:

use std::collections::BTreeMap;

fn main() {
    let mut harga_menu: BTreeMap<&str, u32> = BTreeMap::new();
    harga_menu.insert("martabak manis", 32000);
    harga_menu.insert("martabak telur", 38000);
    harga_menu.insert("es teh", 5000);
    for (menu, harga) in &harga_menu {
        println!("{}: {}", menu, harga);
    }
    println!("{:?}", harga_menu);
}
es teh: 5000
martabak manis: 32000
martabak telur: 38000
{"es teh": 5000, "martabak manis": 32000, "martabak telur": 38000}

Urutannya alfabetis menurut kunci, sama di tiap kali jalan. Ingat: urut menurut kunci, bukan menurut urutan insert. Pakai HashMap kalau cuma perlu mencari lewat kunci (paling umum), BTreeMap kalau hasilnya harus dicetak urut.

⚠️ Jebakan yang sering ketemu

TL;DR: HashMap<K, V> menyimpan pasangan kunci dan nilai, dan butuh use std::collections::HashMap;. insert menyimpan (mengganti nilai lama kalau kuncinya sudah ada), get memberi Some(&nilai) atau None, dan map["kunci"] panic kalau kuncinya gak ada. Untuk menghitung, *map.entry(kunci).or_insert(0) += 1: baris baru mulai dari 0, * mengubah nilainya. Urutan for di HashMap gak bisa diandalkan (rumus hash-nya diacak tiap kali jalan), jadi pakai BTreeMap kalau hasilnya harus urut menurut kunci.

Yang akan kamu pelajari