7. HASH TABLE (DICTIONARY INTERNALS)¶
Definition¶
-
A hash table is a data structure that stores key-value pairs
-
Uses a hash function to map keys to indices (buckets)
-
Average lookup, insert, delete → O(1)
Internal Structure (.NET Dictionary)¶
Dictionary:
int[] buckets
Entry[] entries
Entry:
int hashCode
int next
TKey key
TValue value
Buckets¶
-
Array of indices
-
Each bucket points to an index in
entries
buckets:
[0] = -1
[1] = 2
[2] = -1
[3] = 0
Entries¶
entries:
[0]: hash=23, key=K1, value=V1, next=-1
[1]: hash=45, key=K2, value=V2, next=-1
[2]: hash=23, key=K3, value=V3, next=0
Insert Flow¶
1. Compute hashCode = key.GetHashCode()
2. bucketIndex = hashCode % buckets.Length
3. Check bucket
4. If empty → insert
5. If collision → chain via "next"
Example¶
var dict = new Dictionary<int, string>();
dict[1] = "A";
dict[2] = "B";
Collision Handling¶
-
Uses separate chaining
-
Linked via
nextindex
Bucket[3]:
index 2 → index 0 → null
Lookup Flow¶
1. Compute hash
2. Find bucket
3. Traverse chain
4. Compare using Equals()
Complexity¶
| Case | Time |
|---|---|
| Average | O(1) |
| Worst | O(n) |
Mutable Key Issue¶
class Key
{
public int Id;
public override int GetHashCode() => Id;
}
var k = new Key { Id = 1 };
var dict = new Dictionary<Key, string>();
dict[k] = "A";
k.Id = 2;
dict.ContainsKey(k); // false
Why it fails¶
Original:
hash = 1 → bucket A
After change:
hash = 2 → different bucket
→ lookup fails
Equals vs GetHashCode¶
-
Hash → bucket selection
-
Equals → final match
Equal objects MUST have same hash
Interview Answer¶
A dictionary in .NET is implemented as a hash table using two arrays: buckets and entries. Keys are hashed using GetHashCode to determine the bucket, and collisions are handled using chaining through indices. Lookup is O(1) on average but can degrade if many collisions occur.
8. IEnumerable (ITERATOR INTERNALS)¶
Definition¶
-
Represents a sequence of elements
-
Uses iterator pattern
-
Supports deferred execution
Key Interfaces¶
IEnumerable<T>
IEnumerator<T>
Execution Flow¶
GetEnumerator()
↓
MoveNext()
↓
Current
Example¶
IEnumerable<int> Get()
{
yield return 1;
yield return 2;
}
Compiler Transformation¶
State machine:
state = 0 → return 1
state = 1 → return 2
state = -1 → end
LINQ Chain¶
var result = list
.Where(x => x > 2)
.Select(x => x * 2);
Internal Representation¶
SelectIterator
holds reference to WhereIterator
WhereIterator
holds reference to List
Execution¶
foreach triggers:
Select.MoveNext()
→ calls Where.MoveNext()
→ calls List enumerator
Deferred Execution¶
var query = list.Where(x => x > 2);
No execution yet
query.ToList();
Execution happens here
Interview Answer¶
IEnumerable represents a lazy sequence of elements where execution is deferred until enumeration. LINQ operations form a chain of iterators, and each element flows through the pipeline during iteration.
9. IQueryable¶
Definition¶
-
Represents query as expression tree
-
Execution deferred to provider (e.g., EF, LINQ to SQL)
Example¶
var q = db.Users.Where(x => x.Age > 18);
Expression Tree¶
Where(
source: Users,
predicate: x => x.Age > 18
)
Execution Flow¶
Expression tree
↓
Provider translates (e.g., SQL)
↓
Database executes
↓
Results returned
Difference vs IEnumerable¶
| Feature | IEnumerable | IQueryable |
|---|---|---|
| Execution | In-memory | External |
| Representation | Delegates | Expression trees |
Example Difference¶
// IEnumerable
users.Where(x => x.Age > 18).ToList();
Fetch all → filter in memory
// IQueryable
db.Users.Where(x => x.Age > 18);
Translate → SELECT * WHERE Age > 18
Interview Answer¶
IQueryable builds expression trees that are translated by a provider into optimized queries such as SQL, allowing filtering and execution at the data source rather than in memory.
10. GARBAGE COLLECTION (GC)¶
Definition¶
-
Automatic memory management system
-
Frees memory of objects that are no longer reachable
Key Concept — Reachability¶
Roots:
- stack variables
- static fields
- CPU registers
GC:
marks reachable objects
removes unreachable ones
Example¶
var obj = new Person();
Stack:
obj → 0x100
Heap:
0x100 → Person
obj = null;
Heap object now unreachable → eligible for GC
Generational Model¶
Gen0 → new objects
Gen1 → survivors
Gen2 → long-lived
Flow¶
New object → Gen0
If survives:
→ Gen1
→ Gen2
Collection Behavior¶
| Generation | Frequency | Cost |
|---|---|---|
| Gen0 | frequent | cheap |
| Gen1 | medium | moderate |
| Gen2 | rare | expensive |
GC Phases¶
1. Mark¶
Traverse object graph
Mark reachable objects
2. Sweep¶
Remove unreachable objects
3. Compact¶
Move objects to remove gaps
Example — Compaction¶
Before:
[Obj][free][Obj][free]
After:
[Obj][Obj][free space]
Large Object Heap (LOH)¶
-
Objects > ~85 KB
-
Allocated directly in Gen2
-
Not compacted frequently
Example¶
var arr = new byte[100000];
Trigger Conditions¶
-
Allocation threshold exceeded
-
Memory pressure
-
System conditions
IDisposable vs GC¶
using var file = new FileStream(...);
GC:
manages memory
IDisposable:
releases unmanaged resources
Finalizer¶
~MyClass()
{
}
-
Runs during GC
-
Non-deterministic
-
Performance cost
Memory Leak Example¶
static List<object> cache = new();
Objects always referenced → never collected
Interview Answer¶
The .NET garbage collector is a generational, mark-and-compact system that tracks object reachability starting from root references. Objects are allocated in Gen0 and promoted if they survive collections. Short-lived objects are collected frequently, while long-lived objects are collected less often to optimize performance. The GC manages memory automatically, but unmanaged resources must be handled using IDisposable.