RecentDictionary
RecentDictionary implements a Most Recently Used HashTable. It's usually handy to implement a cache.
Out implementation is based on the work of
Jim Wiese.
Externally it behaves just like a dictionary with two small differences:
- The constructor has a capacity that limits the maximum number of items, the default is 50.
- You can't trust that an item added to the dictionary will be there for long, if not used and capacity is reached, the element will be removed and Purged event fired.
Internally it has a LinkedList that contains all the values:
- Every operation (Add, Contains or the indexer) moves the element to the head
- When a new element is added, and the maximum capacity is reached, the element in the tail of the list is purged.
public class RecentDictionary<K, V>
{
public RecentDictionary()
public RecentDictionary(int capacity)
public void Add(K key, V value)
public bool Contains(K key)
public void Remove(K key)
public V this[K key]{get;set;}
public bool TryGetValue(K key, out V value)
public V GetOrCreate(K key, Func<V> createValue)
public int Capacity{get;set;}
public int Count{get;}
public event Action<K,V> Purged
public override string ToString()
}
Example
Let's imagine we have a few images that are being continuously retrieved in a non-homogeneous fashion.
DirectoryInfo di = new DirectoryInfo(@"C:\Users\Public\Pictures\Sample Pictures");
FileInfo[] files = di.GetFiles();
Random r = new Random();
Console.WriteLine(files.Length);
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < 10000; i++)
{
int index = (int)Math.Sqrt(r.Next(files.Length * files.Length));
string fileName = files[index].FullName;
byte[] fileData = File.ReadAllBytes(files[index].FullName);
}
sw.Stop();
Console.WriteLine(sw.Elapsed)
In order to improve performance, we just create a RecentDictionary and use GetOrCreate in our operation, using fileName as the key.
This way, the most recent 4 items are kept
RecentDictionary<string, byte[]> images = new RecentDictionary<string, byte[]>(4);
sw.Reset();
sw.Start();
for (int i = 0; i < 10000; i++)
{
int index = (int)Math.Sqrt(r.Next(files.Length * files.Length));
string fileName = files[index].FullName;
byte[] fileData = images.GetOrCreate(fileName, () => File.ReadAllBytes(fileName));
}
sw.Stop();
Console.WriteLine("CacheSize {0} Time {1}".Formato(4, sw.Elapsed));
As you see, just by making a cache of 4 elements we have improved performance a 200%
If you are curious, here is the time it takes for different cache sizes in my laptop.