HashMap trong Rust
HashMap lưu dữ liệu dưới dạng các cặp khóa-giá trị (key-value) — bạn tra cứu theo khóa chứ không theo vị trí như với mảng hay vector. Nếu bạn đã dùng dictionary trong Python hay dùng object làm map trong JavaScript, thì đây là cùng một ý tưởng.
Tạo một HashMap
Phần tiêu đề “Tạo một HashMap”use std::collections::HashMap;
fn main() { let mut ages = HashMap::new();
ages.insert("Alice", 30); ages.insert("Bob", 25);
println!("{:?}", ages);}use std::collections::HashMap; ở đầu file đưa kiểu này vào phạm vi — khác với Vec và String, HashMap không có sẵn mặc định, nên dòng này là bắt buộc.
Tra cứu một giá trị
Phần tiêu đề “Tra cứu một giá trị”use std::collections::HashMap;
fn main() { let mut ages = HashMap::new(); ages.insert("Alice", 30);
match ages.get("Alice") { Some(age) => println!("Alice {age} tuổi"), None => println!("Không tìm thấy"), }}.get() trả về một Option thay vì trả thẳng giá trị, vì khóa có thể không tồn tại. Chúng ta sẽ tìm hiểu kỹ về Option trong bài riêng của nó — tạm thời, match với Some/None là cách chuẩn để xử lý.
Cập nhật một giá trị
Phần tiêu đề “Cập nhật một giá trị”Insert lần nữa với cùng khóa sẽ ghi đè giá trị cũ:
use std::collections::HashMap;
fn main() { let mut ages = HashMap::new(); ages.insert("Alice", 30); ages.insert("Alice", 31); // ghi đè 30
println!("{:?}", ages.get("Alice"));}Nếu bạn chỉ muốn thêm giá trị khi khóa chưa tồn tại, hãy dùng .entry():
use std::collections::HashMap;
fn main() { let mut ages = HashMap::new(); ages.insert("Alice", 30);
ages.entry("Alice").or_insert(99); // Alice đã có, nên dòng này không làm gì ages.entry("Bob").or_insert(25); // Bob chưa có, nên dòng này thêm vào
println!("{:?}", ages);}Duyệt qua một HashMap
Phần tiêu đề “Duyệt qua một HashMap”use std::collections::HashMap;
fn main() { let mut ages = HashMap::new(); ages.insert("Alice", 30); ages.insert("Bob", 25);
for (name, age) in &ages { println!("{name} {age} tuổi"); }}Khi nào dùng HashMap
Phần tiêu đề “Khi nào dùng HashMap”Hãy dùng HashMap bất cứ khi nào bạn muốn tra cứu theo tên hay ID thay vì theo vị trí — đếm tần suất từ, ánh xạ tên người dùng sang dữ liệu người dùng, lưu cache kết quả theo khóa. Nếu thứ tự quan trọng hoặc bạn chỉ đang làm việc với một danh sách đơn giản, Vec sẽ phù hợp hơn.
Luyện tập
Phần tiêu đề “Luyện tập”1. Danh bạ điện thoại tí hon
Phần tiêu đề “1. Danh bạ điện thoại tí hon”Lưu ba cái tên và số điện thoại vào một HashMap. Sau đó tra cứu "Ben" (có trong danh bạ) và "Dana" (không có), rồi in một dòng thân thiện cho mỗi người. Gợi ý: duyệt qua ["Ben", "Dana"] và match trên .get(name).
Xem lời giải
use std::collections::HashMap;
fn main() { let mut phone_book = HashMap::new(); phone_book.insert("Ana", "555-1234"); phone_book.insert("Ben", "555-9876"); phone_book.insert("Chen", "555-4321");
for name in ["Ben", "Dana"] { match phone_book.get(name) { Some(number) => println!("{name}: {number}"), None => println!("{name}: không có trong danh bạ"), } }}Kết quả:
Ben: 555-9876Dana: không có trong danh bạDuyệt qua những cái tên ta cần, thay vì duyệt qua map, còn cho ta một thứ tự cố định. Đó là mẹo tiện lợi mỗi khi bạn cần kết quả in ra có thể đoán trước từ một HashMap.
2. Đếm phiếu bầu
Phần tiêu đề “2. Đếm phiếu bầu”Bạn có các phiếu bầu ["đỏ", "xanh dương", "đỏ", "xanh lá", "xanh dương", "đỏ"]. Đếm xem mỗi màu được bao nhiêu phiếu, rồi in tổng của "đỏ", "xanh lá", "xanh dương" và "hồng" (màu không có phiếu nào). Gợi ý: counts.entry(color).or_insert(0) cho bạn số phiếu hiện tại của màu đó, bắt đầu từ 0. Cộng thêm một bằng *count += 1.
Xem lời giải
use std::collections::HashMap;
fn main() { let votes = ["đỏ", "xanh dương", "đỏ", "xanh lá", "xanh dương", "đỏ"]; let mut counts = HashMap::new();
for color in votes { let count = counts.entry(color).or_insert(0); *count += 1; }
for color in ["đỏ", "xanh lá", "xanh dương", "hồng"] { match counts.get(color) { Some(n) => println!("{color}: {n}"), None => println!("{color}: 0"), } }}Kết quả:
đỏ: 3xanh lá: 1xanh dương: 2hồng: 0or_insert(0) trả về một tham chiếu khả biến (mutable reference) tới giá trị được lưu trong map, chứ không phải một bản sao. Dấu * trong *count += 1 nghĩa là “thay đổi con số mà nó trỏ tới”, nên chính map được cập nhật. Mẫu “entry, hoặc insert 0, rồi cộng 1” này là cách chuẩn để đếm trong Rust.
Tóm tắt
Phần tiêu đề “Tóm tắt”HashMap<K, V>lưu các cặp khóa-giá trị; đưa nó vào phạm vi bằnguse std::collections::HashMap;..insert(key, value)thêm mới hoặc ghi đè;.get(key)tra cứu và trả về mộtOption..entry(key).or_insert(value)chỉ thêm khi khóa chưa có.- Thứ tự khi duyệt không được đảm bảo — đừng dựa vào nó.
Kiểm tra nhanh
Phần tiêu đề “Kiểm tra nhanh”Kiểm tra nhanh
1. Khác với Vec hay String, bạn cần gì để đưa HashMap vào phạm vi?
2. .entry(key).or_insert(value) làm gì?
3. Bạn có thể dựa vào thứ tự in ra của các phần tử khi duyệt qua một HashMap không?
Điểm: 0 / 3
Đọc thêm
Phần tiêu đề “Đọc thêm”- Storing Keys with Associated Values in Hash Maps — The Rust Book (tiếng Anh): nói về quyền sở hữu của khóa và giá trị, kèm ví dụ đếm từ có cập nhật giá trị.
- HashMap — Rust by Example (tiếng Anh): một ví dụ danh bạ chạy được, dùng
insert,get,removevà vòng lặp. HashMap— tài liệu thư viện chuẩn (tiếng Anh): mọi phương thức, gồm cảcontains_key,remove,keysvàvalues.Entry— tài liệu thư viện chuẩn (tiếng Anh): mọi thứ bạn làm được với.entry(), nhưor_insert_withvàand_modify.