Categories

Preserved order dictionary

LINQ comes with a powerful extension to simplify coding habits. Remember when we just put a code to import System.Linq into our reference, lots of extension methods come to show while we are browsing the method from the auto-complete message.

The good would also come with some bad. LINQ can hurt the performance. See this http://blogs.msdn.com/shawnhar/archive/2009/07/16/beware-of-the-big-bad-linq.aspx.
As Shawn says, LINQ has more abstraction level so overhead is there to hurt the performance.

After finding information about this and reading his post, I come back and look at my code, then cut out all those unnecessary LINQ operations plus the line “using System.Linq;” also.

For the case I have found in my code is “Dictionary”. As its nature is hashing the key, and put the value mapping to its key by the setting from users. It’s very fast data structure with nearly O(1) for retrieval depends on the hashing algorithm on certain type of key.
Its nature fit my need, but one thing I have faced is to get the first key (or value) from a dictionary which must adhere to the preserved order from insertion by users. Dictionary doesn’t do any sorting, its order can’t be clearly defined and it totally depends on the result of hashing of key storing back in its own data structure.

This issue just jumps back to our discussion about LINQ, by using LINQ, the simplicity comes to live as it provides the method First() using from Keys of Dictionary, this way we just get our first key in no time !!
But behind the scene, we can’t even make any sure of. It usually could hurt our performance. Thus I decide to go in another way, by creating our own preserved order of dictionary called PreservedDictionary class.

It will extend from the Dictionary, and maintains an another single list to store information about keys inserted by users. This way when we want the first key from this PreservedDictionary, we just grab the key from this list and return the result. In the same way, if we want the first value, then we could grab the first key from this list and search the value with this key then return. That’s a good turn around !!

See complete list of PreservedDictionary class below.

/// <summary>
    /// Represents the preserved order dictionary.
    /// </summary>
    public class PreservedDictionary<TKey, TValue> : Dictionary<TKey, TValue>
    {
        /// <summary>
        /// List of preserved key used to retrive the value with preserved order.
        /// </summary>
        protected List<TKey> preservedKeys;
 
        /// <summary>
        /// Gets preserved keys associated with this dictionary.
        /// </summary>
        public List<TKey> PreservedKeys
        {
            get { return preservedKeys; }
        }
 
        /// <summary>
        /// Create a new preserved dictionary.
        /// </summary>
        /// <param name="key">Key</param>
        /// <param name="value">Value</param>
        public PreservedDictionary()
            : base()
        {
            preservedKeys = new List<TKey>();
        }
 
        /// <summary>
        /// Get the first ordered key ordered by the insertion process.
        /// </summary>
        /// <returns>First key if this dictionary contains at least 1 elements, otherwise returns null.</returns>
        public TKey GetFirstKey()
        {
            if (base.Count > 0)
                //guarantee that perservedKeys is the same in size
                return preservedKeys[0];
            else
                return default(TKey);
        }
 
        /// <summary>
        /// Get the first ordered value ordered by the insertion process.
        /// </summary>
        /// <returns>First value if this dictionary contains at least 1 elements, otherwise returns null.</returns>
        public TValue GetFirstValue()
        {
            if (base.Count > 0)
                //guaruntee that perservedKeys is the same in size
                return base[preservedKeys[0]];
            else
                return default(TValue);
        }
 
        /// <summary>
        /// Get the last ordered key ordered by the insertion process.
        /// </summary>
        /// <returns>Last key if this dictionary contains at least 1 elements, otherwise returns null.</returns>
        public TKey GetLastKey()
        {
            if (base.Count > 0)
                //guarantee that perservedKeys is the same in size
                return preservedKeys[base.Count-1];
            else
                return default(TKey);
        }
 
        /// <summary>
        /// Get the last ordered value ordered by the insertion process.
        /// </summary>
        /// <returns>Last value if this dictionary contains at least 1 elements, otherwise returns null.</returns>
        public TValue GetLastValue()
        {
            if (base.Count > 0)
                //guaruntee that perservedKeys is the same in size
                return base[preservedKeys[base.Count-1]];
            else
                return default(TValue);
        }
 
        /// <summary>
        /// Add a new key, value pair.
        /// </summary>
        /// <param name="key">Key</param>
        /// <param name="value">Value</param>
        new public void Add(TKey key, TValue value)
        {
            //add to our perserved keys first
            preservedKeys.Add(key);
 
            base.Add(key, value);
        }
 
        /// <summary>
        /// Remove all the keys, and values from this dictionary.
        /// </summary>
        new public void Clear()
        {
            //clear preserved keys first
            preservedKeys.Clear();
 
            base.Clear();
        }
 
        /// <summary>
        /// Remove the value from the specified key from this dictionary.
        /// </summary>
        /// <param name="value">Key to remove the value from this dictionary.</param>
        new public void Remove(TKey key)
        {
            //remove from preserved keys also
            preservedKeys.Remove(key);
 
            base.Remove(key);
        }
    }

That’s really simple right! We need to maintain only those relevant methods which can affect our keys-list.
We may not want to expose the key-list to outside, this is the choice. And also note that we interact with the generic type here so be neat when doing something.

PreservedDictionary class also provides users to get the last key, or value from the dictionary with preserved order from insertion by users. It turns out that almost all of the time we just need only the first, or the last key or value from the list, not the in between as we usually got the key to grab on somewhere else.

One thing is left from above implementation as its Keys, and Values are not cover and don’t work with our keys-list yet. It involves enumerator to do the job, I need some time to learn about it before give it a change.
But for now the code works and we get the preserved version of dictionary created in-house and ready to be used.

2 comments to Preserved order dictionary

  • Very easy to grab&understand, thank you, Wasin. One thing about LINQ is that its purpose is not to overcome the performance of other prevalent method. Though it not seem to be best (at least not too bad for business use) optimized (depend on the type of works), it provides very great readability and extensibility when working other developers. I love the combination of LINQ and extension methods the most enabled others developers to declaratively write the code from your provided framework, for example. It helps them and me (in the future) to write a sugar-syntatic code. If the framework is hard to use, no body’d love it. This is my thought.

  • Thanks for your word winladen.

    I consider those LINQ methods in the situation which needs much of efficiency. Totally agrees that it will make dev-team more consider on the purpose and easily to collaborate more. Anyway I can’t rely on the LINQ methods to ensure the speed of the engine itself.

    Anyway, thanks for your suggestion, and your note on this such issue.

    Thanks again :)

Leave a Reply

 

 

 

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">