Hash Map
A HashMap is a data structure that provides a highly efficient way to store and retrieve key-value pairs. It's often used when you need to associate unique keys with specific values and want fast access to the values based on their keys.
-
Data type to store data in Key - Value pair.
-
Keys are used to looking up corresponding values.
-
Hashing: The keys are processed through a hash function, which converts the key into an index (or hash code) that determines where the key-value pair is stored in the underlying array.
-
Dynamic Resizing: HashMap dynamically resizes its internal array to maintain performance as the number of elements grows.
-
Non-Sequential: The order of elements in a HashMap is not guaranteed to be the same as the order in which they were inserted. The order is determined by the hash codes of the keys.
Example: Phone book - Search for the name and get the phone number
Under the hood, a hash function determines how to store data so the value can be quickly located.
In other languages, we have a similar feature.
Map / Dictionary / Associative Array
Rules
- All values must have the same data type.
- Each key can only have one value associated with it at a time.
- No duplicate keys.
What is Hashing??
Hashing is a process of transforming any given input (such as a string or a file) into a fixed-size string of characters, which is typically a sequence of numbers and letters.
Fixed-Size Output: No matter the size of the input, the output (called the hash value) will always have a fixed size.
Uniqueness: Ideally, different inputs should produce different hash values, although collisions (where two different inputs produce the same hash value) can happen.
Efficiency: Hashing is designed to be fast and efficient, making it useful for quick data retrieval.
Hasing Example
use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; fn main() { let s = "hello world"; // Create a hasher let mut hasher = DefaultHasher::new(); // Hash the string s.hash(&mut hasher); // Get the resulting hash as a u64 let hash_value = hasher.finish(); println!("Hash value for '{}': {}", s, hash_value); }
HashMap Example
use std::collections::HashMap; fn main() { let mut cities = HashMap::new(); cities.insert("Glassboro", 30000); cities.insert("Mullicahill", 25000); cities.insert("Swedesboro", 28000); println!("cities is {:?}", cities); }
Insert
use std::collections::HashMap; fn main() { let mut cities = HashMap::new(); cities.insert("Glassboro", 30000); cities.insert("Mullicahill", 25000); cities.insert("Swedesboro", 28000); println!("cities is {:?}", cities); //Option 1 - Insert / Overwrite Existing Value cities.insert("Paulsboro", 11000); cities.insert("Glassboro", 31000); println!("cities is {:?}", cities); }
Insert/Update/Get
use std::collections::HashMap; fn main() { let mut cities = HashMap::new(); cities.insert("Glassboro", 30000); cities.insert("Mullicahill", 25000); cities.insert("Swedesboro", 28000); //Option 1 - Update / Overwrite Existing Value cities.insert("Glassboro", 31000); //Option 2 - Insert a new entry if it doesn't exist cities.entry("Depford").or_insert(12000); //Option 3 - Get the value of the Key and perform a mathematical operation let gpopulation = cities.entry("Glassboro").or_insert(0); *gpopulation += 1; //print the hash map values. The print order may or may not be the same //as the insert. It changes from time to time. println!("cities is {:?}", cities); let glassboro_population = cities.get("Glassboro"); if glassboro_population.is_some(){ println!("glassboro_population is {:?}", glassboro_population); } else if glassboro_population.is_none(){ println!("glassboro_population is not available", ); } }
Remove
use std::collections::HashMap; fn main() { let mut cities = HashMap::new(); cities.insert("Glassboro", 30000); cities.insert("Mullicahill", 25000); cities.insert("Swedesboro", 28000); // Remove an entry cities.remove("Mullicahill"); println!("After removal: {:?}", cities); }
Iterating Over the HashMap
use std::collections::HashMap; fn main() { let mut cities = HashMap::new(); cities.insert("Glassboro", 30000); cities.insert("Mullicahill", 25000); cities.insert("Swedesboro", 28000); // Iterating over the HashMap for (city, population) in &cities { println!("The population of {} is {}", city, population); } }
Check for Existence
use std::collections::HashMap; fn main() { let mut cities = HashMap::new(); cities.insert("Glassboro", 30000); cities.insert("Mullicahill", 25000); cities.insert("Swedesboro", 28000); // Checking if a key exists if cities.contains_key("Swedesboro") { println!("Swedesboro is in the HashMap"); } else { println!("Swedesboro is not in the HashMap"); } }
Merge
use std::collections::HashMap; fn main() { let mut cities1 = HashMap::new(); cities1.insert("Glassboro", 30000); cities1.insert("Mullicahill", 25000); let mut cities2 = HashMap::new(); cities2.insert("Swedesboro", 28000); cities2.insert("Depford", 12000); // Merging two HashMaps for (city, population) in cities2 { cities1.insert(city, population); } println!("Merged cities: {:?}", cities1); }