Hashmaps

HashMap

std

HashMap is a key-value data structure. It allows you to store data in an unordered collection, where each element is identified by a unique key. This makes HashMap an excellent choice for lookups, insertions, and deletions based on keys.

All of the hashmap keys must have the same type as each other, and all of the values must have the same type.

//! This example demonstrates the basic usage of `HashMap` in Rust.
//!
//! `HashMap` is a collection that stores key-value pairs, where each key is
//! unique. It's useful for scenarios where you need to quickly look up values
//! based on keys.
//!
//! This example covers creating, inserting, updating, accessing, iterating, and
//! removing elements from a `HashMap`.

use std::collections::HashMap;

fn main() {
    // Create a new, empty HashMap called 'scores'.
    let mut scores = HashMap::new();

    // Insert a key-value pair into the HashMap.
    // The key is "Blue" (a `String`), and the value is 10 (an `i32`).
    scores.insert(String::from("Blue"), 10);

    // Update the value associated with the key "Blue".
    // The previous value will be replaced with 25.
    scores.insert(String::from("Blue"), 25);

    // Get the value associated with 'team_name' from the HashMap.
    let team_name = String::from("Blue");
    // `get()` returns an `Option<&i32>`.
    // `copied()` converts it to `Option<i32>` by copying the value.
    // `unwrap_or(0)` returns the value if it exists, or 0 if it doesn't.
    let _score = scores.get(&team_name).copied().unwrap_or(0);

    // Another way to access values using pattern matching:
    let score = scores.get(&team_name);
    match score {
        Some(s) => println!("Team {} score: {}", team_name, s),
        None => println!("Team {} not found", team_name),
    }

    // Iterate over and print the key-value pairs in the `HashMap`
    // (in arbitrary order). '&scores' borrows the `HashMap` immutably.
    for (key, value) in &scores {
        println!("{key}: {value}");
    }

    // Check if a key exists.
    if scores.contains_key(&String::from("Blue")) {
        println!("Blue team exists");
    }

    // Getting the length.
    println!("Number of teams: {}", scores.len());

    // Insert a key-value pair only if the key doesn't already exist.
    // `entry()` returns an `Entry` enum, and `or_insert` inserts the value if
    // the key is absent, otherwise it does nothing.
    scores.entry(String::from("Yellow")).or_insert(50);

    // Remove a key and return its value if present.
    if let Some(removed_value) = scores.remove(&String::from("Blue")) {
        println!("Removed Blue team with score: {}", removed_value);
    }

    // Clear the HashMap.
    scores.clear();
    println!("After clearing, number of teams: {}", scores.len());
}

HashSet

std

use std::collections::HashSet;

fn main() {
    // Creating a new `HashSet`.
    let mut languages = HashSet::new();

    // Inserting values.
    languages.insert("Rust");
    languages.insert("Python");
    languages.insert("JavaScript");

    // Inserting duplicate values (ignored).
    let added = languages.insert("Rust");
    println!("Was 'Rust' added? {}", added); // false, already exists.

    // Creating from iterators
    let numbers = vec![1, 2, 3, 4, 5, 1, 2, 3];
    let unique_numbers: HashSet<_> = numbers.into_iter().collect();
    println!("Unique numbers: {:?}", unique_numbers); // 1, 2, 3, 4, 5 (order may vary).

    // Checking if an element exists.
    if languages.contains("Rust") {
        println!("Rust is in the set");
    }

    // Removing elements.
    if languages.remove("Python") {
        println!("Python was removed from the set");
    }

    // Iterating over elements.
    for language in &languages {
        println!("{}", language);
    }

    // Set operations.
    let set1: HashSet<_> = [1, 2, 3].iter().cloned().collect();
    let set2: HashSet<_> = [3, 4, 5].iter().cloned().collect();

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

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

    // Difference.
    let difference: HashSet<_> = set1.difference(&set2).cloned().collect();
    println!("Difference (set1 - set2): {:?}", difference); // 1, 2.

    // Symmetric difference (elements in either set but not both).
    let symmetric_difference: HashSet<_> =
        set1.symmetric_difference(&set2).cloned().collect();
    println!("Symmetric Difference: {:?}", symmetric_difference); // 1, 2, 4, 5.

    // Check if a set is a subset of another.
    let subset: HashSet<_> = [1, 2].iter().cloned().collect();
    println!(
        "Is {:?} a subset of {:?}? {}",
        subset,
        set1,
        subset.is_subset(&set1)
    ); // true

    // Get the length.
    println!("Number of languages: {}", languages.len());

    // Clear the set.
    languages.clear();
    println!("After clearing, languages: {:?}", languages); // {}
}

HashMap and HashSet with Custom Hash Function

std

//! Demonstrates how to use a custom hash function (FNV) with `HashMap` and
//! `HashSet` for better performance with small keys.
//!
//! **Warning:** FNV is not suitable if there is a risk of denial-of-service
//! attacks. Be certain that your program is not exposed to malicious inputs.
//!
//! Add to Cargo.toml: fnv = "1.0.7" # Or latest

use std::collections::HashMap;
use std::collections::HashSet;

fn main() {
    let mut fnv_map = HashMap::with_hasher(fnv::FnvBuildHasher::default());
    fnv_map.insert("key1", "value1");
    fnv_map.insert("key2", "value2");

    // Or simpler:
    let mut _map: fnv::FnvHashMap<&str, &str> = fnv::FnvHashMap::default();

    // Similarly for HashSet:
    let mut fnv_set = fnv::FnvHashSet::default();
    fnv_set.insert("item1");
    fnv_set.insert("item2");

    // You may also pre-allocate capacity for better performance:
    let mut _map_with_capacity: HashMap<String, String> =
        HashMap::with_capacity(100);
    let mut _set_with_capacity: HashSet<String> = HashSet::with_capacity(100);

    // Or set both capacity and a custom hasher:
    let mut map =
        fnv::FnvHashMap::with_capacity_and_hasher(10, Default::default());
    map.insert(1, "one");
    map.insert(2, "two");
}

HashMap Using a Custom Type as the Key

std

//! Demonstrate using a custom type as the key in a `HashMap`.

use std::collections::HashMap;
use std::hash::Hash;
use std::hash::Hasher;

// Custom struct that will be used as a key.
#[derive(Debug, PartialEq, Eq)]
struct Student {
    id: u32,
}

// `Hash` and `Eq` traits must be implemented
// in order to use the struct as a `HashMap` key.
// BEWARE: If two keys are equal, their hashes must be equal.
// This is a fundamental requirement for hash-based collections.
// k1 == k2 -> hash(k1) == hash(k2)
impl Hash for Student {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.id.hash(state);
    }
}

fn main() {
    // Create a new HashMap to store student grades.
    let mut grades = HashMap::new();

    // Create some student instances.
    let alice = Student { id: 1 };
    let bob = Student { id: 2 };

    // Insert student grades into the HashMap.
    grades.insert(alice, 95);
    grades.insert(bob, 87);

    // Look up a student's grade using their ID.
    // Note that we create a new Student instance for lookup,
    // but it's considered the same key if the IDs match.
    let lookup_student = Student { id: 1 };
    if let Some(grade) = grades.get(&lookup_student) {
        println!("Student with ID 1 got grade: {}", grade);
    }
}

Related Data Structures

  • B-trees.
  • Other Maps.

Related Topics

  • Concurrent Data Structures.
  • Data Structures.
  • Databases.
  • Hashing.
  • Key-Value Stores.