Click here to Skip to main content
15,891,689 members
Articles / Programming Languages / C#
Article

A KeyedList implementation

Rate me:
Please Sign up or sign in to vote.
4.83/5 (26 votes)
24 Dec 20032 min read 160.5K   869   38   35
A KeyedList implements an ordered key-value list.

Introduction

A KeyedList is an ordered key-value list.  In comparison:

  • Hashtable is a key-value list that is not ordered;
  • SortedList is a key-value list that is sorted;
  • ArrayList is an ordered list.

but in the System.Collections namespace, there is nothing that implements an ordered key-value list.

Hasn't Someone Done This Before?

For some reason, Microsoft has decided to implement a KeyedList as part System.Web.UI namespace.  Refer to this preliminary MSDN documentation which is part of Longhorn.  What's up with implementing this in System.Web.UI?  What I need is a KeyedList that is platform independent!  Googling a bit more, I came across a code implementation from the Mono project, here and here

The Implementation

The following code takes that implementation, written by Todd Berman, removes the IStateManager interface and places the implementation into the System.Collections namespace, rather than System.Web.UI. Rocket science this isn't, but since I didn't find any KeyedList articles on CP, I figured here would be a good place to put this valuable class.  The KeyedList is also derived from the interfaces specified in the Longhorn MSDN rather than the interfaces used in the Mono implementation.  All told, I think I spent more time Googling for an ordered Hashtable than I did extracting the code from the Mono links previously mentioned and slightly modifying them!

using System;
using System.Collections;

namespace System.Collections
{
 public interface IOrderedDictionary
 {
  void Insert(Int32 index, Object key, Object value);
  void RemoveAt(Int32 index);
 }

 [Serializable]
 public class KeyedList : 
    ICollection, IDictionary, IEnumerable, IOrderedDictionary
 {
  private Hashtable objectTable = new Hashtable ();
  private ArrayList objectList = new ArrayList ();

  public void Add (object key, object value)
  {
   objectTable.Add (key, value);
   objectList.Add (new DictionaryEntry (key, value));
  }

  public void Clear ()
  {
   objectTable.Clear ();
   objectList.Clear ();
  }

  public bool Contains (object key)
  {
   return objectTable.Contains (key);
  }

  public void CopyTo (Array array, int idx)
  {
   objectTable.CopyTo (array, idx);
  }

  public void Insert (int idx, object key, object value)
  {
   if (idx > Count)
    throw new ArgumentOutOfRangeException ("index");

   objectTable.Add (key, value);
   objectList.Insert (idx, new DictionaryEntry (key, value));
  }

  public void Remove (object key)
  {
   objectTable.Remove (key);
   objectList.RemoveAt (IndexOf (key));
  }

  public void RemoveAt (int idx)
  {
   if (idx >= Count)
    throw new ArgumentOutOfRangeException ("index");

   objectTable.Remove ( ((DictionaryEntry)objectList[idx]).Key );
   objectList.RemoveAt (idx);
  }

  IDictionaryEnumerator IDictionary.GetEnumerator ()
  {
   return new KeyedListEnumerator (objectList);
  }

  IEnumerator IEnumerable.GetEnumerator ()
  {
   return new KeyedListEnumerator (objectList);
  }

  public int Count 
  {
   get { return objectList.Count; }
  }

  public bool IsFixedSize 
  {
   get { return false; }
  }

  public bool IsReadOnly 
  {
   get { return false; }
  }

  public bool IsSynchronized 
  {
   get { return false; }
  }

  public object this[int idx] 
  {
   get { return ((DictionaryEntry) objectList[idx]).Value; }
   set 
   {
    if (idx < 0 || idx >= Count)
     throw new ArgumentOutOfRangeException ("index");

    object key = ((DictionaryEntry) objectList[idx]).Key;
    objectList[idx] = new DictionaryEntry (key, value);
    objectTable[key] = value;
   }
  }

  public object this[object key] 
  {
   get { return objectTable[key]; }
   set 
   {
    if (objectTable.Contains (key))
    {
     objectTable[key] = value;
     objectTable[IndexOf (key)] = new DictionaryEntry (key, value);
     return;
    }
    Add (key, value);
   }
  }

  public ICollection Keys 
  {
   get 
   { 
    ArrayList retList = new ArrayList ();
    for (int i = 0; i < objectList.Count; i++)
    {
     retList.Add ( ((DictionaryEntry)objectList[i]).Key );
    }
    return retList;
   }
  }

  public ICollection Values 
  {
   get 
   {
    ArrayList retList = new ArrayList ();
    for (int i = 0; i < objectList.Count; i++)
    {
     retList.Add ( ((DictionaryEntry)objectList[i]).Value );
    }
    return retList;
   }
  }
 
  public object SyncRoot 
  {
   get { return this; }
  } 

  private int IndexOf (object key)
  {
   for (int i = 0; i < objectList.Count; i++)
   {
    if (((DictionaryEntry) objectList[i]).Key.Equals (key))
    {
     return i;
    }
   }
   return -1;
  }
 }

 public class KeyedListEnumerator : IDictionaryEnumerator
 {
  private int index = -1;
  private ArrayList objs;

  internal KeyedListEnumerator (ArrayList list)
  {
   objs = list;
  }

  public bool MoveNext ()
  {
   index++;
   if (index >= objs.Count)
    return false;

   return true;
  }

  public void Reset ()
  {
   index = -1;
  }

  public object Current 
  {
   get 
   {
    if (index < 0 || index >= objs.Count)
     throw new InvalidOperationException ();

    return objs[index];
   }
  }

  public DictionaryEntry Entry 
  {
   get 
   {
    return (DictionaryEntry) Current;
   }
  }

  public object Key 
  {
   get 
   {
    return Entry.Key;
   }
  }

  public object Value 
  {
   get 
   {
    return Entry.Value;
   }
  }
 }
}

Using The KeyedList

Using the KeyedList is just like using a Hashtable, except that when you enumerate through the list, the entries are in the same order as when they were added to the list.  Believe me, I needed this capability!

using System;
using System.Collections;

namespace KeyedListTest
{
 class ConsoleApp
 {
  [STAThread]
  static void Main(string[] args)
  {
   KeyedList k=new KeyedList();
   k.Add("One", 1);
   k.Add("Two", 2);
   k.Add("Three", 3);
   k.Add("Four", 4);
   k.Add("Five", 5);
   k.Add("Six", 6);
   k.Add("Seven", 7);
   k.Add("Eight", 8);
   k.Add("Nine", 9);
   k.Add("Ten", 10);

   foreach(DictionaryEntry entry in k)
   {
    Console.WriteLine(entry.Key+": "+entry.Value.ToString());
   }
  }
 }
}

Sample screenshot

Conclusion

That's it!  A bit of googling, a bit of massaging of some existing source code, and voila!, a solution to my particular problem, and maybe one day a solution to your problem as well.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Architect Interacx
United States United States
Blog: https://marcclifton.wordpress.com/
Home Page: http://www.marcclifton.com
Research: http://www.higherorderprogramming.com/
GitHub: https://github.com/cliftonm

All my life I have been passionate about architecture / software design, as this is the cornerstone to a maintainable and extensible application. As such, I have enjoyed exploring some crazy ideas and discovering that they are not so crazy after all. I also love writing about my ideas and seeing the community response. As a consultant, I've enjoyed working in a wide range of industries such as aerospace, boatyard management, remote sensing, emergency services / data management, and casino operations. I've done a variety of pro-bono work non-profit organizations related to nature conservancy, drug recovery and women's health.

Comments and Discussions

 
GeneralOne Suggestion Pin
scadaguy30-Dec-03 5:33
scadaguy30-Dec-03 5:33 
QuestionStrongly-Typed Collection? Pin
mikasa30-Dec-03 4:31
mikasa30-Dec-03 4:31 
AnswerRe: Strongly-Typed Collection? Pin
Marc Clifton30-Dec-03 4:49
mvaMarc Clifton30-Dec-03 4:49 
GeneralRe: Strongly-Typed Collection? Pin
mikasa30-Dec-03 5:05
mikasa30-Dec-03 5:05 
GeneralRe: Strongly-Typed Collection? Pin
Iftekhar Ivaan18-Feb-04 9:47
Iftekhar Ivaan18-Feb-04 9:47 
GeneralRe: Strongly-Typed Collection? Pin
mikasa18-Feb-04 9:54
mikasa18-Feb-04 9:54 
GeneralCouple of comments Pin
Frank Olorin Rizzi27-Dec-03 4:14
Frank Olorin Rizzi27-Dec-03 4:14 
GeneralRe: Couple of comments Pin
Marc Clifton27-Dec-03 10:18
mvaMarc Clifton27-Dec-03 10:18 
Frank Olorin Rizzi wrote:
Why use the System.Collection namespace instead of your own ?

Good point, and I debated that. Well, it's easily changed! On the other hand, I can't understand why Microsoft is putting this in the Web.UI namespace. What--this class is only useful for web applications?

Frank Olorin Rizzi wrote:
since it keeps 2 copies of each object

Well, no. It keeps two references to an object. That takes up very little space.

Frank Olorin Rizzi wrote:
If you store only the references, then you end up in the various possible problems due to aliasing.

I'm not exactly sure what you mean here. If the object is removed from the KeyedList, it's removed from both the Array and the Hashtable at the same time.

I don't think either of these two points are an issue. Managing references is really not memory intensive. If memory is an issue, then the approach should be changed--why would you want to handle a million objects in memory? The Longhorn documentation also indicates that uses both an Array and a Hashtable, so I don't think Microsoft thinks there's a problem here either.

Frank Olorin Rizzi wrote:
since the next version of C# should allow for templated classes, and this will be especially useful with collections, how does this class fit in that scheme?

Obviously, it doesn't. Nor does any of the other classes in the Collections namespace. When templates show up, all those "object" variables simply get turned into "<t> t" variables and the class definition changes to something like "class KeyedList<t>" (I forget the actual template syntax at this point).

But I'm also not sure templates are necessary in all cases. The enumeration capabilities is already good enough to automatically cast an object to the specified type:

foreach (XmlSchemaObjectCollection schemaObjects in coll) {...}

The real problem is automatically converting the value type in the key list collections when you index them like:

object foo=schemaObject["XmlSchemaComplexType'];

Which is something that you can do much nicer, without casting, with templates.

Frank Olorin Rizzi wrote:
Anyway, as I said, I think this is very usefull.

Yup. It sure came in handy for me!

Marc

Latest AAL Article
My blog
Join my forum!
GeneralRe: Couple of comments Pin
Frank Olorin Rizzi27-Dec-03 11:49
Frank Olorin Rizzi27-Dec-03 11:49 
GeneralBookmarked Pin
Jonathan de Halleux26-Dec-03 5:39
Jonathan de Halleux26-Dec-03 5:39 
Generalno duplicate keys Pin
Ashley van Gerven25-Dec-03 15:51
Ashley van Gerven25-Dec-03 15:51 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.