//  home   //  advanced search   //  news   //  categories   //  sql build chart   //  downloads   //  statistics
 ASP FAQ 
Home
ASP FAQ Tutorials

   8000XXXX Errors
   ASP.NET 2.0
      Basic Language Constructs
      Themes & Skins (web)
      Tips & Techniques
      XML Serialization
   Classic ASP 1.0
   Databases
   General Concepts
   Search Engine Optimization (SEO)

Contact Us
Site Map

Search

Web
aspfaq.com
tutorials.aspfaq.com
asp.net2.aspfaq.com

ASP FAQ Tutorials :: ASP.NET 2.0 :: Basic Language Constructs :: Using Hashtables


Using Hashtables

Overview

Hashtables have got to be the greatest thing since sliced bread.  Especially for VB programmers, they also are surpisingly under-utilized.  Every programmer should learn how to effectively use hashtables as they will likely prove useful in a wide variety of situations.

Hashtables are an extremely effecient way of storing key/value pairs.  The key can be an integer (similar to an array), but because of how Hashtables work, the key can actually be anything from a string to an object.

Let's assume for example that you had a class that represented a list of people, and you wanted to be able to access that list of people based on their social security number.  You could store them in an array (or array list) and then iterate over that list each time you wanted to access on of those people.  The two most pressing problems are that this type of access is both cumbersome and inefficient.

A common use of hashtables is to collect other classes.  For example, you might have a class the represents a person, and another class that represents a collection of people.  Hashtables can be used to reference the specific people, to allow you to very quickly access specific people based criteria like name or Social Security Number.

For Example

Let's say that you had  a class that represents a person.  This class has two public member variables (name & ssn (social security number)) as follows:

CSharp

public class Person
{
    public Person(string name, string ssn)
    {
        this.Name = name;
        this.SSN = ssn;
    }

    public string Name;
    public string SSN;
}

VB


Public Class Person
    Public  Sub New(ByVal name As String, ByVal ssn As String)
        Me.Name = name
        Me.SSN = ssn
    End Sub
 
    Public Name As String
    Public SSN As String
End Class


We could create a list of "people" by just creating an array of person as follows:

Person[] People;

We could also create a new class called people as follows.  In order to allow multiple people to be managed, we need a public method called Add that would take a Person object as a parameter.   We can then use an array list to store the actual person.

CSharp

public class People     // Could also be called PersonCollection
{
  public People()
  {
     People = new ArrayList();
  }
  public ArrayList People;

  public void Add(Person person) {
    People.Add(person);
  }
}

VB


Public Class People ' Could also be called PersonCollection
  Private Function People() As Public
     People = New ArrayList()
  End Function
  Public People As ArrayList
 
  Public  Sub Add(ByVal person As Person)
    People.Add(person)
  End Sub
End Class


Now, we can create objects of type person, and add them to the collection of people as follows:

CSharp

// Create the people collection
People people = new People();

// Create a person and add them to the collection
Person ej = new Person("EJ Alexandra", "775-11-1234");
people.Add(ej)

// Create another person and add them
Person peter = new Person("Peter Jackson", "123-45-6789");
people.Add(peter);

VB

' Create the people collection
Dim people As People =  New People() 
 
' Create a person and add them to the collection
Dim ej As Person =  New Person("EJ Alexandra","775-11-1234") 
people.Add(ej)
 
' Create another person and add them
Dim peter As Person =  New Person("Peter Jackson","123-45-6789") 
people.Add(peter)


The problem is that without iterating over all of the people in the collection, how do you find 'ej' or the person with the social security number '775-11-1234'. 

Enter the Hashtable

This is where hashtables really start to shine.  Let's say that we not only used an array list to keep track of the people, but also used a Hashtable, who's key was based on name and ssn.   The "People" class would look like this:

CSharp

public class People  // Could also be called PersonCollection
{
    public People()
    {
        m_byName = new Hashtable();
        m_bySSN = new Hashtable();
        m_byIndex = new ArrayList();
    }  

    private Hashtable m_byName;
    private Hashtable m_bySSN;
    private ArrayList m_byIndex;

    public Person ByName(string name)
    {
        return (Person)m_byName[name];
    }

    public Person BySS(string ssn)
    {
        return (Person)m_bySSN[ssn];
    }

    public void Add(Person person)
    {
        m_byName.Add(person.Name, person);
        m_bySSN.Add(person.SSN, person);
        m_byIndex.Add(person);
    }

    public void Add(string name, string ssn)
    {
        Person person = new Person(name, ssn);
        Add(person);
    }
}

VB


Public Class People ' Could also be called PersonCollection
    Private Function People() As Public
        m_byName = New Hashtable()
        m_bySSN = New Hashtable()
        m_byIndex = New ArrayList()
    End Function
 
    Private m_byName As Hashtable
    Private m_bySSN As Hashtable
    Private m_byIndex As ArrayList
 
    Public Function ByName(ByVal name As String) As Person
        Return CType(m_byName(name), Person)
    End Function
 
    Public Function BySS(ByVal ssn As String) As Person
        Return CType(m_bySSN(ssn), Person)
    End Function
 
    Public  Sub Add(ByVal person As Person)
        m_byName.Add(person.Name, person)
        m_bySSN.Add(person.SSN, person)
        m_byIndex.Add(person)
    End Sub
 
    Public  Sub Add(ByVal name As String, ByVal ssn As String)
        Dim person As Person =  New Person(name,ssn) 
        Add(person)
    End Sub
End Class


Notice that in addition to the ArrayList that we had before, we also now have 2 hashtables.  m_byName and m_bySSN

When a new person is added, it adds them to the array list, and to the two hashtables.  The 1st parameter is the key (Name or SSN), and the 2nd parameter is the object that is being stored. 

This let's us also add 2 acces methods ByName and BySSN.  By just passing in a name or SSN, these access methods can very easily return the associated Person, by just passing that key into the appropriate Hashtable.  The code is very simple, easy to undersand and very efficient on the back end of things.

Final Steps

The final step that makes this class even more helpful, is to make the People class inherit from the IEnumerable class.  This will allow us to interate over the collection of people, just like any other collection as follows:

CSharp

// We can iterate over each Person in the People collection as follows:

foreach (Person person in people) {
  // Print person to the screen
  Response.Write(person.Name + " - " + person.SSN + "<br>");
}

VB

' We can iterate over each Person in the People collection as follows:
 
Dim person As Person
For Each person In people
  ' Print person to the screen
  Response.Write(person.Name + " - " + person.SSN + "<br>")
Next


For more information about foreach loops, see this article.  Using for and foreach with arrays, array...

In order for this to work, we need to inherit from the interface IEnumerable and provide a GetEnumerator() method.   people.Count returns how many people have been added.   The full people class would look like this:

CSharp

// We can iterate over each Person in the People collection as follows:

public class People : IEnumerable // Could also be called PersonCollection
{
    public People()
    {
        m_byName = new Hashtable();
        m_bySSN = new Hashtable();
        m_byIndex = new ArrayList();
    }

    private Hashtable m_byName;
    private Hashtable m_bySSN;
    private ArrayList m_byIndex;

    public Person ByName(string name)
    {
        return (Person)m_byName[name];
    }

    public Person BySS(string ssn)
    {
        return (Person)m_bySSN[ssn];
    }

    public Person this[int index]
    {
        get
        {
            return (Person)m_byIndex[index];
        }
    }

    public void Add(Person person)
    {
        m_byName.Add(person.Name, person);
        m_bySSN.Add(person.SSN, person);
        m_byIndex.Add(person);
    }

    public void Add(string name, string ssn)
    {
        Person person = new Person(name, ssn);
        Add(person);
    }

    public int Count()
    {
        return m_byIndex.Count;
    }

    public IEnumerator GetEnumerator()
    {
        return m_byIndex.GetEnumerator();
    }
}

VB

' We can iterate over each Person in the People collection as follows:
 
Public Class People
     Implements IEnumerable ' Could also be called PersonCollection
    Public  Sub New()
        m_byName = New Hashtable()
        m_bySSN = New Hashtable()
        m_byIndex = New ArrayList()
    End Sub
 
    Private m_byName As Hashtable
    Private m_bySSN As Hashtable
    Private m_byIndex As ArrayList
 
    Public Function ByName(ByVal name As String) As Person
        Return CType(m_byName(name), Person)
    End Function
 
    Public Function BySS(ByVal ssn As String) As Person
        Return CType(m_bySSN(ssn), Person)
    End Function
 
    Default Public ReadOnly Property Item(index As Integer) As Person
        Get 
            Return CType(m_byIndex(index), Person)
        End Get
    End Property
 
    Public  Sub Add(ByVal person As Person)
        m_byName.Add(person.Name, person)
        m_bySSN.Add(person.SSN, person)
        m_byIndex.Add(person)
    End Sub
 
    Public  Sub Add(ByVal name As String, ByVal ssn As String)
        Dim person As Person =  New Person(name,ssn) 
        Add(person)
    End Sub
 
    Public Function Count() As Integer
        Return m_byIndex.Count
    End Function
 
    Public Function GetEnumerator() As IEnumerator
        Return m_byIndex.GetEnumerator()
    End Function
End Class


Implementation example

This web page provides two textboxes (TextBox1 and TextBox2) that add pesons's to a collection of people which is stored in the Session object.  On pre-render, the collection of people is printed in a listbox.

default.aspx
<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        Name:<br />
        <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox><br />
        SSN:
        <br />
        <asp:TextBox ID="TextBox2" runat="server"></asp:TextBox>
        <br />
        <asp:Button ID="Button1" runat="server" Text="Add Person" OnClick="Button1_Click" /><br />
        <br />
        <br />
        People:<br />
        <asp:ListBox ID="ListBox1" runat="server" Height="221px"
            Width="169px"></asp:ListBox></div>
    </form>
</body>
</html>

default.aspx.cs/vb

CSharp

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

public partial class _Default : System.Web.UI.Page 
{
    private People people;

    protected void Page_Load(object sender, EventArgs e)
    {
        if (Session["people"] == null) Session["people"] = new People();
        people = (People)Session["people"];
    }

    protected override void OnPreRender(EventArgs e)
    {
        base.OnPreRender(e);
        ListBox1.Items.Clear();
        foreach (Person person in people)
        {
            ListBox1.Items.Add(String.Format("{0} - {1}", 
                person.Name, person.SSN));
        }
    }

    protected void Button1_Click(object sender, EventArgs e)
    {
        people.Add(TextBox1.Text, TextBox2.Text);
    }
}

VB

Imports System
Imports System.Data
Imports System.Configuration
Imports System.Web
Imports System.Web.Security
Imports System.Web.UI
Imports System.Web.UI.WebControls
Imports System.Web.UI.WebControls.WebParts
Imports System.Web.UI.HtmlControls
 
Public partial Class _Default
     Inherits System.Web.UI.Page
    Private people As People
 
    Protected  Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs)
        If Session("people") Is Nothing Then
             Session("people") = New People()
        End If
        people = CType(Session("people"), People)
    End Sub
 
    Protected Overrides  Sub OnPreRender(ByVal e As EventArgs)
        MyBase.OnPreRender(e)
        ListBox1.Items.Clear()
        Dim person As Person
        For Each person In people
            ListBox1.Items.Add(String.Format("{0} - {1}", 
                person.Name, person.SSN))
        Next
    End Sub
 
    Protected  Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs)
        people.Add(TextBox1.Text, TextBox2.Text)
    End Sub
End Class


Exceptions & Error Checking with Hashtables

This code does not do any error checking.  Some specific things that would need to be checked have to do with how Hashtables handle duplicate keys.  Specifically, they are not accepted.  What this means is that before adding a key/value pair to  a hashtable, you should really check that they key does not already exist.  If you do no, you will likely get an error like the following.

Server Error in '/WebSite1' Application.

Item has already been added. Key in dictionary: 'xxx'  Key being added: 'xxx'

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.ArgumentException: Item has already been added. Key in dictionary: 'xxx'  Key being added: 'xx'

The way to avoid this error is to check that they key does not exist before adding it as follows:

CSharp

 public void Add(Person person)
 {
   // Check that the name is not already used as a key, add if so
   if (!m_byName.ContainsKey(person.Name)) m_byName.Add(person.Name, person);

   // Check that the key is not already used as a key, add if so
   if (!m_bySSN.ContainsKey(person.SSN)) m_bySSN.Add(person.SSN, person);

   // Always add the person to the array list...
   m_byIndex.Add(person);
 }

VB


 Public  Sub Add(ByVal person As Person)
   ' Check that the name is not already used as a key, add if so
   If Not m_byName.ContainsKey(person.Name) Then
        m_byName.Add(person.Name, person)
   End If
 
   ' Check that the key is not already used as a key, add if so
   If Not m_bySSN.ContainsKey(person.SSN) Then
        m_bySSN.Add(person.SSN, person)
   End If
 
   ' Always add the person to the array list...
   m_byIndex.Add(person)
 End Sub


Also, the reason that I always include an array list in addition to any Hashtables that I use is that order is not preserved with a hashtable.  In other words, I could add keys 1, 2 and 3 and if I access the hashtable by index, it might return 3, 1 and 2.  The array list on the other hand will always return the elements in the order in which they were added.  This can be important if trying to preserve a specific order.

Conclusion

In closing, I think you can see that hashtables are extraordinarly powerful.  They should be used where appropriate and will make a powerful tool in your toolbox under the right circumstances.  Good luck.


'----------------------------------------------------------------
' Converted from C# to VB .NET using CSharpToVBConverter(1.2).
' Developed by: Kamal Patel (http://www.KamalPatel.net)
'----------------------------------------------------------------

Related Articles

Using for and foreach with arrays, arraylists and other collections

 

 


Created: 6/7/2006 | Last Updated: 6/7/2006 | broken links | helpful | not helpful | statistics
© Copyright 2006, UBR, Inc. All Rights Reserved. (616)

 

Copyright 1999-2006, All rights reserved.
Finding content
Finding content.  An error has occured...