Dot Net Perls

Queue Collection - C#

by Sam Allen

Problem

Remember the last 100 items accessed and store them in a data structure. The furthest back item must be evicted to make space for the newest. You want good performance and small memory footprint.

Solution: C#

We need the Queue object for our solution. First, let's look at some methods this class has. I am going to show the Enqueue and Dequeue methods in the following example of the built-in Queue. Make sure you include System.Collections.Generic at the top of your file.

//
// 1.
//
Queue<string> queue = new Queue<string>();
//
// 2.
//
queue.Enqueue("box"); // contains the string "box"
queue.Enqueue("cat"); // "cat" and then "box"
//
// 3.
//
string removedString = queue.Dequeue(); // now contains "cat" only
  1. New Queue
    In the start of the method above, we create a new Queue generic object. It stores the type of string, which is in the brackets.
  2. Enqueue is called
    This adds the specified argument string to the contents of the Queue. Enqueue is next called again.
  3. Dequeue does the opposite
    Dequeue removes the first queued item and then returns the object removed.

How can I combine Queue and Dictionary?

Here we combine the Queue with a Dictionary to achieve a cache that both has constant O(1) access time, and also the FIFO behavior of Queue. This is important because it can yield the optimizations of both data structures. It may be overly complex for many scenarios, however.

class QueueCache<T, V> : CollectionBase
{
    Queue<T> _qInner = new Queue<T>();
    Dictionary<T, V> _dInner = new Dictionary<T, V>();

    int _sizeLimit;

    public QueueCache()
        : this(25)
    {
    }

    public QueueCache(int sizeLimitIn)
    {
        //
        // Use Debug.Assert for testing.
        //
        Debug.Assert(sizeLimitIn > 0);
        _sizeLimit = sizeLimitIn;
    }

    public bool TryGetValue(T keyIn, out V outParam)
    {
        return _dInner.TryGetValue(keyIn, out outParam);
    }

    public V AddToCache(T keyCache, V valToCache)
    {
        //
        // Add to our Dictionary and Queue. If we grow too large,
        // remove from both the Queue and the Dictionary.
        //
        _qInner.Enqueue(keyCache);
        _dInner.Add(keyCache, valToCache);

        if (_qInner.Count > _sizeLimit)
        {
            T rem = _qInner.Dequeue();
            _dInner.Remove(rem);
        }
        return valToCache;
    }
}

How can I use the above class?

At the start of this page, I showed how to use a C# Queue straight from the built-in types, and in this section I show you how to use the custom QueueCache generic. The following example method does some actions with the QueueCache and uses the TryGetValue method.

class TestClass
{
    //
    // Custom field.
    // Note the syntax at the end with the brackets and parenthesis.
    //
    QueueCache<string, string> _neighborCache = new QueueCache<string, string>(10);

    private string TestMethod(string word)
    {
        //
        // Does the cache contain a value for the key "word"?
        //
        string cache;
        if (_neighborCache.TryGetValue(word, out cache))
        {
            return cache;
        }

        //
        // Add the value "cache" to the queue cache.
        // The queue will never grow larger than 25 elements.
        //
        cache = "Value";
        return _neighborCache.AddToCache(word, cache);
    }
}

Discussion

One use for Queue is a history menu in a Windows Forms application that displays the last 15 unique items visited. As I show, you can combine Queue with Dictionary to achieve some of the benefits of both. FIFO caches can be very useful.

Dot Net Perls
About
Sitemap
Source code
RSS
Algorithms
Pi
A-Star Pathfinding
Bool Sorting
Bit Counting Algorithms
Anagram Code
Recent
Pi
NGEN Installer Class
List Element Equality
DateTime Tips and Tricks
Remove HTML Tags From String
© 2008 Sam Allen. All rights reserved.