Chuyển tới nội dung chính

🎯 HashSet: Tập Hợp Duy Nhất

🎯 Mục Tiêu Bài Học

Sau khi hoàn thành bài học này, bạn sẽ:

  • ✅ Hiểu được HashSet là gì và khi nào dùng
  • ✅ Tạo và sử dụng HashSet
  • ✅ Insert, remove, contains
  • ✅ Set operations: union, intersection, difference
  • ✅ So sánh HashSet vs Vec vs HashMap
  • ✅ Áp dụng HashSet trong code thực tế

🤔 HashSet Là Gì?

Ẩn Dụ Cuộc Sống: Danh Sách Khách Mời

HashSet giống như danh sách khách mời tiệc cưới:

📋 Danh Sách Khách Mời:

  • Mỗi người chỉ có 1 lần (không trùng lặp)
  • Kiểm tra nhanh: "Người này có được mời không?"
  • Thêm/xóa tên dễ dàng
  • Không quan tâm thứ tự

🎯 HashSet Trong Rust:

  • Mỗi giá trị unique
  • Lookup O(1) (trung bình)
  • Insert/Remove O(1)
  • Không có order

Ví Dụ Cơ Bản

use std::collections::HashSet;

fn main() {
let mut guests = HashSet::new();

guests.insert("Alice");
guests.insert("Bob");
guests.insert("Alice"); // Trùng - không thêm!

println!("Total guests: {}", guests.len()); // 2

if guests.contains("Alice") {
println!("Alice is invited!");
}
}

Đầu ra:

Total guests: 2
Alice is invited!

📦 Tạo HashSet

new() Method

use std::collections::HashSet;

fn main() {
let mut set: HashSet<i32> = HashSet::new();

set.insert(1);
set.insert(2);
set.insert(3);

println!("{:?}", set);
}

Từ Array/Vec

use std::collections::HashSet;

fn main() {
let numbers = vec![1, 2, 3, 2, 1, 4];
let unique: HashSet<_> = numbers.into_iter().collect();

println!("{:?}", unique); // {1, 2, 3, 4}
}

Với Capacity

use std::collections::HashSet;

fn main() {
let mut set = HashSet::with_capacity(10);

set.insert("value");

println!("Capacity: {}", set.capacity());
}

🔧 Basic Operations

Insert

use std::collections::HashSet;

fn main() {
let mut set = HashSet::new();

// insert returns bool (true nếu giá trị mới)
let inserted1 = set.insert("apple");
let inserted2 = set.insert("apple"); // Trùng

println!("First insert: {}", inserted1); // true
println!("Second insert: {}", inserted2); // false

println!("{:?}", set);
}

Đầu ra:

First insert: true
Second insert: false
{"apple"}

Contains

use std::collections::HashSet;

fn main() {
let mut set = HashSet::new();
set.insert("rust");
set.insert("python");

if set.contains("rust") {
println!("Rust is in the set!");
}

if !set.contains("java") {
println!("Java is NOT in the set");
}
}

Remove

use std::collections::HashSet;

fn main() {
let mut set = HashSet::new();
set.insert(10);
set.insert(20);
set.insert(30);

println!("Before: {:?}", set);

let removed = set.remove(&20);
println!("Removed 20: {}", removed); // true

let removed2 = set.remove(&99);
println!("Removed 99: {}", removed2); // false

println!("After: {:?}", set);
}

Len và Is Empty

use std::collections::HashSet;

fn main() {
let mut set = HashSet::new();

println!("Empty: {}", set.is_empty()); // true

set.insert(1);
set.insert(2);

println!("Length: {}", set.len()); // 2
println!("Empty: {}", set.is_empty()); // false
}

🔄 Set Operations

Union - Hợp

use std::collections::HashSet;

fn main() {
let a: HashSet<_> = [1, 2, 3].iter().collect();
let b: HashSet<_> = [3, 4, 5].iter().collect();

let union: HashSet<_> = a.union(&b).collect();
println!("Union: {:?}", union); // {1, 2, 3, 4, 5}
}

Intersection - Giao

use std::collections::HashSet;

fn main() {
let a: HashSet<_> = [1, 2, 3, 4].iter().collect();
let b: HashSet<_> = [3, 4, 5, 6].iter().collect();

let intersection: HashSet<_> = a.intersection(&b).collect();
println!("Intersection: {:?}", intersection); // {3, 4}
}

Difference - Hiệu

use std::collections::HashSet;

fn main() {
let a: HashSet<_> = [1, 2, 3, 4].iter().collect();
let b: HashSet<_> = [3, 4, 5, 6].iter().collect();

// Elements in A but not in B
let diff: HashSet<_> = a.difference(&b).collect();
println!("A - B: {:?}", diff); // {1, 2}

// Elements in B but not in A
let diff2: HashSet<_> = b.difference(&a).collect();
println!("B - A: {:?}", diff2); // {5, 6}
}

Symmetric Difference - Hiệu Đối Xứng

use std::collections::HashSet;

fn main() {
let a: HashSet<_> = [1, 2, 3, 4].iter().collect();
let b: HashSet<_> = [3, 4, 5, 6].iter().collect();

// Elements in A or B but not both
let sym_diff: HashSet<_> = a.symmetric_difference(&b).collect();
println!("Symmetric diff: {:?}", sym_diff); // {1, 2, 5, 6}
}

Is Subset / Is Superset

use std::collections::HashSet;

fn main() {
let a: HashSet<_> = [1, 2].iter().collect();
let b: HashSet<_> = [1, 2, 3, 4].iter().collect();

println!("A is subset of B: {}", a.is_subset(&b)); // true
println!("B is superset of A: {}", b.is_superset(&a)); // true
println!("A is disjoint from B: {}", a.is_disjoint(&b)); // false
}

Is Disjoint - Rời Nhau

use std::collections::HashSet;

fn main() {
let a: HashSet<_> = [1, 2, 3].iter().collect();
let b: HashSet<_> = [4, 5, 6].iter().collect();
let c: HashSet<_> = [3, 4, 5].iter().collect();

println!("A and B disjoint: {}", a.is_disjoint(&b)); // true
println!("A and C disjoint: {}", a.is_disjoint(&c)); // false (có 3)
}

🔁 Iteration

Iterate Over Values

use std::collections::HashSet;

fn main() {
let mut set = HashSet::new();
set.insert("apple");
set.insert("banana");
set.insert("cherry");

for item in &set {
println!("{}", item);
}
}

Lưu ý: Thứ tự không được đảm bảo!

Collect from Iterator

use std::collections::HashSet;

fn main() {
let numbers = vec![1, 2, 2, 3, 3, 3, 4];

// Remove duplicates
let unique: HashSet<_> = numbers.iter().collect();

println!("{:?}", unique); // {1, 2, 3, 4}
}

🎯 Ví Dụ Thực Tế

Ví Dụ 1: Remove Duplicates

use std::collections::HashSet;

fn remove_duplicates(numbers: Vec<i32>) -> Vec<i32> {
let set: HashSet<_> = numbers.into_iter().collect();
set.into_iter().collect()
}

fn main() {
let numbers = vec![1, 2, 2, 3, 4, 4, 5];
let unique = remove_duplicates(numbers);

println!("{:?}", unique);
}

Ví Dụ 2: Find Common Elements

use std::collections::HashSet;

fn find_common(list1: &[i32], list2: &[i32]) -> Vec<i32> {
let set1: HashSet<_> = list1.iter().collect();
let set2: HashSet<_> = list2.iter().collect();

set1.intersection(&set2)
.map(|&&x| x)
.collect()
}

fn main() {
let a = vec![1, 2, 3, 4, 5];
let b = vec![3, 4, 5, 6, 7];

let common = find_common(&a, &b);
println!("Common elements: {:?}", common); // [3, 4, 5]
}

Ví Dụ 3: Word Uniqueness

use std::collections::HashSet;

fn unique_words(text: &str) -> usize {
let words: HashSet<_> = text
.split_whitespace()
.map(|w| w.to_lowercase())
.collect();

words.len()
}

fn main() {
let text = "Hello world hello Rust world";
let count = unique_words(text);

println!("Unique words: {}", count); // 3 (hello, world, rust)
}

Ví Dụ 4: Visited Tracker

use std::collections::HashSet;

struct WebCrawler {
visited: HashSet<String>,
}

impl WebCrawler {
fn new() -> Self {
WebCrawler {
visited: HashSet::new(),
}
}

fn visit(&mut self, url: &str) -> bool {
if self.visited.contains(url) {
println!("Already visited: {}", url);
false
} else {
println!("Visiting: {}", url);
self.visited.insert(url.to_string());
true
}
}

fn stats(&self) {
println!("Total pages visited: {}", self.visited.len());
}
}

fn main() {
let mut crawler = WebCrawler::new();

crawler.visit("https://example.com");
crawler.visit("https://example.com/page1");
crawler.visit("https://example.com"); // Already visited

crawler.stats();
}

Đầu ra:

Visiting: https://example.com
Visiting: https://example.com/page1
Already visited: https://example.com
Total pages visited: 2

Ví Dụ 5: Tag System

use std::collections::HashSet;

struct Article {
title: String,
tags: HashSet<String>,
}

impl Article {
fn new(title: &str) -> Self {
Article {
title: title.to_string(),
tags: HashSet::new(),
}
}

fn add_tag(&mut self, tag: &str) {
self.tags.insert(tag.to_string());
}

fn has_tag(&self, tag: &str) -> bool {
self.tags.contains(tag)
}

fn common_tags(&self, other: &Article) -> Vec<String> {
self.tags
.intersection(&other.tags)
.cloned()
.collect()
}
}

fn main() {
let mut article1 = Article::new("Rust Basics");
article1.add_tag("rust");
article1.add_tag("programming");
article1.add_tag("tutorial");

let mut article2 = Article::new("Rust Advanced");
article2.add_tag("rust");
article2.add_tag("advanced");
article2.add_tag("programming");

let common = article1.common_tags(&article2);
println!("Common tags: {:?}", common); // ["rust", "programming"]
}

Ví Dụ 6: Access Control

use std::collections::HashSet;

struct User {
username: String,
permissions: HashSet<String>,
}

impl User {
fn new(username: &str) -> Self {
User {
username: username.to_string(),
permissions: HashSet::new(),
}
}

fn grant_permission(&mut self, perm: &str) {
self.permissions.insert(perm.to_string());
}

fn has_permission(&self, perm: &str) -> bool {
self.permissions.contains(perm)
}

fn has_all_permissions(&self, required: &[&str]) -> bool {
required.iter().all(|&p| self.has_permission(p))
}
}

fn main() {
let mut user = User::new("alice");

user.grant_permission("read");
user.grant_permission("write");

if user.has_permission("read") {
println!("User can read");
}

if user.has_all_permissions(&["read", "write"]) {
println!("User can read and write");
}

if !user.has_permission("delete") {
println!("User cannot delete");
}
}

📊 HashSet vs Vec vs HashMap

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

fn main() {
// Vec - ordered, allows duplicates
let vec = vec![1, 2, 2, 3];
println!("Vec: {:?}", vec); // [1, 2, 2, 3]

// HashSet - unordered, unique values only
let set: HashSet<_> = vec![1, 2, 2, 3].into_iter().collect();
println!("HashSet: {:?}", set); // {1, 2, 3}

// HashMap - unordered, key-value pairs
let mut map = HashMap::new();
map.insert("key", "value");
println!("HashMap: {:?}", map); // {"key": "value"}
}
CollectionOrderDuplicatesLookupUse Case
Vec✅ Ordered✅ AllowedO(n)Sequential data
HashSet❌ Unordered❌ Unique onlyO(1)Unique values
HashMap❌ UnorderedKeys uniqueO(1)Key-value pairs

💻 Bài Tập Thực Hành

Bài 1: Unique Elements

use std::collections::HashSet;

fn main() {
let numbers = vec![1, 2, 3, 2, 4, 3, 5];

// TODO: Tạo HashSet từ vec để loại bỏ duplicates
// let unique: HashSet<_> = ...

// TODO: In ra số lượng unique elements
}
💡 Gợi ý
use std::collections::HashSet;

fn main() {
let numbers = vec![1, 2, 3, 2, 4, 3, 5];

let unique: HashSet<_> = numbers.into_iter().collect();

println!("Unique count: {}", unique.len()); // 5
println!("Unique elements: {:?}", unique);
}

Bài 2: Set Operations

use std::collections::HashSet;

fn main() {
let set_a: HashSet<_> = [1, 2, 3, 4, 5].iter().collect();
let set_b: HashSet<_> = [4, 5, 6, 7, 8].iter().collect();

// TODO: Tìm union
// TODO: Tìm intersection
// TODO: Tìm difference (A - B)
}
💡 Gợi ý
use std::collections::HashSet;

fn main() {
let set_a: HashSet<_> = [1, 2, 3, 4, 5].iter().collect();
let set_b: HashSet<_> = [4, 5, 6, 7, 8].iter().collect();

let union: HashSet<_> = set_a.union(&set_b).collect();
println!("Union: {:?}", union);

let intersection: HashSet<_> = set_a.intersection(&set_b).collect();
println!("Intersection: {:?}", intersection);

let difference: HashSet<_> = set_a.difference(&set_b).collect();
println!("Difference: {:?}", difference);
}

Bài 3: Email Validator

use std::collections::HashSet;

fn has_unique_emails(emails: &[&str]) -> bool {
// TODO: Check if all emails are unique
// Hint: Compare Vec length with HashSet length
}

fn main() {
let emails1 = vec!["[email protected]", "[email protected]"];
let emails2 = vec!["[email protected]", "[email protected]"];

println!("List 1 unique: {}", has_unique_emails(&emails1));
println!("List 2 unique: {}", has_unique_emails(&emails2));
}
💡 Gợi ý
use std::collections::HashSet;

fn has_unique_emails(emails: &[&str]) -> bool {
let set: HashSet<_> = emails.iter().collect();
set.len() == emails.len()
}

fn main() {
let emails1 = vec!["[email protected]", "[email protected]"];
let emails2 = vec!["[email protected]", "[email protected]"];

println!("List 1 unique: {}", has_unique_emails(&emails1)); // true
println!("List 2 unique: {}", has_unique_emails(&emails2)); // false
}

🎯 Tóm Tắt

MethodMô TảReturn
new()Tạo HashSet mớiHashSet<T>
insert(v)Thêm valuebool (true nếu mới)
remove(&v)Xóa valuebool
contains(&v)Kiểm tra tồn tạibool
len()Số lượng elementsusize
is_empty()Kiểm tra rỗngbool
union()Hợp hai setsIterator
intersection()Giao hai setsIterator
difference()Hiệu hai setsIterator
symmetric_difference()Hiệu đối xứngIterator
is_subset()Kiểm tra subsetbool
is_superset()Kiểm tra supersetbool
is_disjoint()Kiểm tra rời nhaubool

Quy tắc vàng:

  • ✅ HashSet cho unique values
  • ✅ O(1) lookup, insert, remove
  • ✅ Values phải implement Eq + Hash
  • ✅ Order không được đảm bảo
  • ✅ Dùng cho: deduplication, membership tests, set operations

🔗 Liên Kết Hữu Ích


Bài tiếp theo: Iterators →

Trong bài tiếp theo, chúng ta sẽ tìm hiểu về Iterators - công cụ mạnh mẽ để xử lý sequences!

Loading comments...