Last active
December 17, 2024 09:05
-
-
Save aannenko/0afb2c12a68e2399ad3febe63bb05f17 to your computer and use it in GitHub Desktop.
A Dictionary class that uses an array of linked lists to hold the key-value pairs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var myDict = new NaiveDictionary<int, string?>(); | |
myDict[1] = "abc"; | |
myDict[1] = "def"; | |
myDict[2] = "def"; | |
myDict[3] = "def"; | |
myDict[4] = "def"; | |
myDict[5] = "def"; | |
myDict[6] = "def"; | |
myDict[7] = "def"; | |
myDict[8] = "def"; | |
myDict[9] = "def"; | |
myDict[10] = "def"; | |
myDict[11] = "def"; | |
myDict[12] = "def"; | |
myDict[13] = "def"; | |
myDict[14] = "def"; | |
myDict[15] = "def"; | |
myDict[16] = "def"; | |
myDict[17] = "def"; | |
myDict[18] = "def"; | |
myDict[1].Dump(); | |
myDict[2].Dump(); | |
myDict[3].Dump(); | |
myDict[4].Dump(); | |
myDict[5].Dump(); | |
myDict[6].Dump(); | |
myDict[7].Dump(); | |
myDict[8].Dump(); | |
myDict[18].Dump(); | |
myDict[19].Dump(); | |
sealed class NaiveDictionary<TKey, TValue> where TKey : notnull, IEquatable<TKey> | |
{ | |
private LinkedList<(TKey Key, TValue? Value)>[] _buckets = new LinkedList<(TKey, TValue?)>[4]; | |
private int _count = 0; | |
public TValue? this[TKey key] | |
{ | |
get | |
{ | |
if (TryGetValue(key, out var val)) | |
return val; | |
throw new ArgumentException("Key does not exist"); | |
} | |
set | |
{ | |
SetInternal(key, value, true); | |
} | |
} | |
public bool TryGetValue(TKey key, out TValue? value) | |
{ | |
ArgumentNullException.ThrowIfNull(key); | |
var bucket = _buckets[key.GetHashCode() % _buckets.Length]; | |
if (bucket is not null) | |
{ | |
foreach (var (k, v) in bucket) | |
{ | |
if (key.Equals(k)) | |
{ | |
value = v; | |
return true; | |
} | |
} | |
} | |
value = default; | |
return false; | |
} | |
public bool Add(TKey key, TValue? value) => | |
SetInternal(key, value, true); | |
public bool TryAdd(TKey key, TValue? value) => | |
SetInternal(key, value, false); | |
private bool SetInternal(TKey key, TValue? value, bool replaceExisting = false) | |
{ | |
ArgumentNullException.ThrowIfNull(key); | |
GrowBucketsIfNeeded(); | |
var bucketIndex = key.GetHashCode() % _buckets.Length; | |
var bucket = _buckets[bucketIndex] ?? (_buckets[bucketIndex] = new()); | |
for (var node = bucket.First; node != null; node = node.Next) | |
{ | |
if (node.Value.Key.Equals(key)) | |
{ | |
if (replaceExisting) | |
{ | |
node.Value = new(key, value); | |
return true; | |
} | |
else | |
{ | |
return false; | |
} | |
} | |
} | |
bucket.AddLast((key, value)); | |
_count++; | |
return true; | |
} | |
private void GrowBucketsIfNeeded() | |
{ | |
if (_count * 0.75 < _buckets.Length) | |
return; | |
_count = 0; | |
var oldBuckets = _buckets; | |
var newBuckets = new LinkedList<(TKey Key, TValue? Value)>[oldBuckets.Length * 2]; | |
foreach (var bucket in oldBuckets) | |
{ | |
if (bucket is null || bucket.Count == 0) | |
continue; | |
foreach (var (key, value) in bucket) | |
{ | |
var newBucketIndex = key.GetHashCode() % newBuckets.Length; | |
LinkedList<(TKey, TValue?)> newBucket; | |
if (newBuckets[newBucketIndex] is null) | |
{ | |
_count++; | |
newBucket = newBuckets[newBucketIndex] = new(); | |
} | |
else | |
{ | |
newBucket = newBuckets[newBucketIndex]; | |
} | |
newBucket.AddLast((key, value)); | |
} | |
} | |
_buckets = newBuckets; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment