Click here to Skip to main content
6,635,160 members and growing! (13,952 online)
Email Password   helpLost your password?
General Programming » Algorithms & Recipes » Data Structures     Intermediate

A KeyedList implementation

By Marc Clifton

A KeyedList implements an ordered key-value list.
C#, .NET, Win2K, WinXP, Win2003, Visual Studio, Dev
Posted:24 Dec 2003
Views:94,289
Bookmarked:34 times
Announcements
Loading...
 
Search    
Advanced Search
Add to IE Search
printPrint   add Share
      Discuss Discuss   Broken Article?Report  
26 votes for this article.
Popularity: 6.25 Rating: 4.42 out of 5
2 votes, 7.7%
1
1 vote, 3.8%
2
2 votes, 7.7%
3
3 votes, 11.5%
4
18 votes, 69.2%
5

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

About the Author

Marc Clifton


Member
Marc is the creator of two open source projets, MyXaml, a declarative (XML) instantiation engine and the Advanced Unit Testing framework, and Interacx, a commercial n-tier RAD application suite.  Visit his website, www.marcclifton.com, where you will find many of his articles.

Marc lives in Hudson, NY with his son Ian, who attends the Hawthorne Valley School. To contact Marc, email him at marc.clifton@gmail.com.
Occupation: Architect
Company: Interacx
Location: United States United States

Other popular Algorithms & Recipes articles:

Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
 Msgs 1 to 25 of 34 (Total in Forum: 34) (Refresh)FirstPrevNext
GeneralSmall bug PinmemberDThrasher9:01 28 Dec '07  
GeneralGenerics version PinsupporterMarc Clifton10:36 28 Dec '07  
Generalgreatly simplified C# version PinmemberBrian Perrin1:20 1 Mar '07  
GeneralRe: greatly simplified C# version PinsupporterMarc Clifton10:28 28 Dec '07  
GeneralCode update, tiny but could be usefull Pinmemberraymond7723:07 25 Jan '07  
GeneralThanks! PinmemberMicah Wedemeyer5:37 10 Oct '06  
GeneralContainsKey Pinmemberericksoa045:19 27 Mar '06  
General.NET 2.0 KeyedList PinsupporterMarc Clifton6:22 27 Mar '06  
GeneralGetEnumerator? PinmemberDoncp17:45 1 Jan '06  
GeneralCode Update Pinmemberdavid.minor10:21 25 Jul '05  
GeneralCode Update Pinmemberdavid.minor10:19 25 Jul '05  
GeneralSimilar Implementation in C Pinmemberdavid.minor10:18 25 Jul '05  
GeneralHashtableList - derives from hashtable PinmemberMartyOne11:47 1 Feb '05  
GeneralRe: HashtableList - derives from hashtable PinmemberMartyOne12:01 1 Feb '05  
GeneralRe: HashtableList - derives from hashtable PinmemberMartyOne13:03 1 Feb '05  
GeneralFixed a bug PinsussJakob Røjel21:28 30 Jan '05  
GeneralAttempted to convert to vb.net PinmemberKenJohnson18:21 30 Jul '04  
GeneralThe CopyTo method should maybe return an ordered list as well? PinmemberTrond Reierth17:03 4 Jul '04  
Generali think SortedList is good enough for use Pinmembersuger23:32 27 Jun '04  
GeneralRe: i think SortedList is good enough for use Pinmembersuger0:01 28 Jun '04  
Generalenven easier Pinmembersuger20:23 11 Jul '04  
GeneralBug in this[object key] set property Pinmemberdwhearn7:18 8 Jan '04  
GeneralRe: Bug in this[object key] set property Pinmemberrohan_p15:14 30 Nov '04  
GeneralOne Suggestion PinmemberBrian Gideon6:33 30 Dec '03  
GeneralStrongly-Typed Collection? Pinmembermikasa5:31 30 Dec '03  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 24 Dec 2003
Editor: Nishant Sivakumar
Copyright 2003 by Marc Clifton
Everything else Copyright © CodeProject, 1999-2009
Web21 | Advertise on the Code Project