HashMap Implementation from Scratch in DSA C# - Time & Space Complexity
When building a HashMap from scratch, it is important to understand how fast it can add, find, or remove items as the data grows.
We want to know how the time to do these operations changes when the number of stored items increases.
Analyze the time complexity of the following code snippet.
public class SimpleHashMap {
private readonly LinkedList>[] buckets;
private readonly int size = 10;
public SimpleHashMap() {
buckets = new LinkedList>[size];
}
public void Put(int key, string value) {
int index = key % size;
if (buckets[index] == null) buckets[index] = new LinkedList>();
buckets[index].AddLast(new KeyValuePair(key, value));
}
}
This code creates a simple HashMap that stores key-value pairs in buckets based on a hash function. It adds items to a linked list in the bucket.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Adding an item to a linked list inside a bucket after computing the bucket index.
- How many times: Each Put operation does a constant time hash calculation and then adds to the linked list. But if many keys collide, the linked list grows and searching it takes longer.
Explain the growth pattern intuitively.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 1 operation per insert (small linked lists) |
| 100 | Operations grow if many keys collide, linked lists get longer |
| 1000 | Longer linked lists cause more operations per insert or search |
Pattern observation: If keys spread evenly, operations stay close to constant. If many collide, operations grow linearly with number of items in a bucket.
Time Complexity: O(1) on average, O(n) in worst case
On average, adding or finding an item takes constant time, but if many keys collide, it can take time proportional to the number of items.
[X] Wrong: "HashMap operations always take the same fixed time no matter what."
[OK] Correct: If many keys end up in the same bucket, operations slow down because the linked list grows and must be searched.
Understanding how hash functions and collisions affect speed shows you know the real behavior of HashMaps beyond theory. This skill helps you explain trade-offs clearly in interviews.
"What if we replaced the linked list with a balanced tree in each bucket? How would the time complexity change?"
Practice
HashMap in data structures?Solution
Step 1: Understand the role of HashMap
A HashMap stores data as key-value pairs, allowing quick access to values using keys.Step 2: Compare with other data structures
Unlike lists or arrays, HashMaps do not store data sequentially or sorted but focus on fast lookup.Final Answer:
To store key-value pairs for fast data retrieval -> Option BQuick Check:
HashMap = key-value fast access [OK]
- Confusing HashMap with sorted structures
- Thinking HashMap stores data sequentially
- Assuming HashMap is for calculations
Solution
Step 1: Recall C# standard HashMap equivalent
In C#,Dictionary<K,V>is the built-in generic collection similar to HashMap.Step 2: Check syntax correctness
Dictionary map = new Dictionary(); correctly declares a Dictionary with int keys and string values.Final Answer:
Dictionary map = new Dictionary(); -> Option DQuick Check:
C# HashMap = Dictionary [OK]
- Using non-existent HashMap class in C#
- Confusing Hashtable with generic Dictionary
- Wrong generic syntax
class SimpleHashMap {
private int[] keys = new int[5];
private string[] values = new string[5];
public void Put(int key, string value) {
int index = key % 5;
keys[index] = key;
values[index] = value;
}
public string Get(int key) {
int index = key % 5;
if (keys[index] == key) return values[index];
return null;
}
}
SimpleHashMap map = new SimpleHashMap();
map.Put(1, "One");
map.Put(6, "Six");
string result = map.Get(1);
What is the value of result after this code runs?Solution
Step 1: Calculate index for key 1
Index = 1 % 5 = 1. So keys[1] = 1, values[1] = "One".Step 2: Calculate index for key 6
Index = 6 % 5 = 1. This overwrites keys[1] = 6, values[1] = "Six".Step 3: Get value for key 1
Index = 1 % 5 = 1. keys[1] is now 6, not 1, soGetreturns null.Final Answer:
null -> Option AQuick Check:
Collision overwrites key 1, so Get(1) = null [OK]
- Ignoring key collision overwrite
- Assuming values are stored separately
- Expecting 'One' despite collision
Put method and choose the correct fix:
public void Put(int key, string value) {
int index = key % 5;
if (keys[index] != key) {
keys[index] = key;
values[index] = value;
}
}Solution
Step 1: Understand current behavior
Theif (keys[index] != key)inserts only if the existing key differs: overwrites on collisions (data loss for previous keys) and skips updates for matching keys.Step 2: Identify collision handling
Without collision handling like linear probing, new keys that hash to occupied slots overwrite existing data.Step 3: Fix by adding linear probing
Linear probing searches next slots to find an empty or matching key slot, preventing overwrites.Final Answer:
Add linear probing to handle collisions -> Option CQuick Check:
Collision fix = linear probing [OK]
- Removing if causes overwrites
- Changing modulus without resizing array
- Wrong if condition logic
class MyHashMap {
private List>[] buckets;
private int size = 10;
public MyHashMap() {
buckets = new List>[size];
for (int i = 0; i < size; i++) buckets[i] = new List>();
}
private int GetBucketIndex(string key) {
return Math.Abs(key.GetHashCode()) % size;
}
public void Put(string key, int value) {
int index = GetBucketIndex(key);
var bucket = buckets[index];
for (int i = 0; i < bucket.Count; i++) {
if (bucket[i].Key == key) {
bucket[i] = new KeyValuePair(key, value);
return;
}
}
bucket.Add(new KeyValuePair(key, value));
}
public int? Get(string key) {
int index = GetBucketIndex(key);
var bucket = buckets[index];
foreach (var kvp in bucket) {
if (kvp.Key == key) return kvp.Value;
}
return null;
}
}
MyHashMap map = new MyHashMap();
map.Put("apple", 5);
map.Put("banana", 3);
map.Put("apple", 10);
int? val = map.Get("apple");
What is the value of val after this code runs?Solution
Step 1: Understand Put method behavior
When inserting "apple" the second time, the code finds the existing key and updates its value to 10.Step 2: Understand Get method behavior
Get("apple") searches the bucket and returns the updated value 10.Final Answer:
10 -> Option AQuick Check:
Updated value for "apple" = 10 [OK]
- Returning old value instead of updated
- Ignoring collision handling with buckets
- Returning null if key exists
Ready to prove it?
Pass the test and this pattern moves unknown
