DekGenius.com
[ Team LiB ] Previous Section Next Section

Recipe 3.5 Making a Type Sortable

Problem

You have a data type that will be stored as elements in an array or an ArrayList. You would like to use the Array.Sort and ArrayList.Sort methods to allow for a custom sorting of your data types in the array. In addition, you may need to use this structure in a SortedList collection.

Solution

Implement the IComparable interface. The following class, Square, implements this interface in a way so that the Array, ArrayList, and SortedList objects can sort and search an array or collection of these Square objects:

public class Square : IComparable
{
    public Square( ){}
    public Square(int height, int width)
    {
        this.height = height;
        this.width = width;
    }

    private int height;
    private int width;

    public int Height
    {
        get{ return (height); }
        set{ height = value; }
    }

    public int Width
    {
        get{ return (width); }
        set{ width = value; }
    }

    public int CompareTo(object obj)
    {
        if (this.GetType( ) != obj.GetType( ))
        {
            throw (new ArgumentException(
                   "Both objects being compared must be of type Square."));
        }
        else
        {
            Square square2 = (Square)obj;

            long area1 = this.Height * this.Width;
            long area2 = square2.Height * square2.Width;

            if (area1 == area2)
            {
                return (0);
            }
            else if (area1 > area2)
            {
                return (1);
            }
            else if (area1 < area2)
            {
                return (-1);
            }
            else
            {
                return (-1);
            }
        }
    }

    public override string ToString( )
    {
        return ("Height:" + height + "  Width:" + width);
    }
}

Discussion

By implementing the IComparable interface on your class (or structure), you can take advantage of the sorting routines of the Array, ArrayList, and SortedList classes. The algorithms for sorting are built into these classes; all you have to do is tell them how to sort through your classes via the code you implement in the IComparable.CompareTo method.

When an array of Square objects is passed to the Array.Sort static method, the array is sorted using the IComparable interface of the Square objects. The same goes for the ArrayList.Sort method. The Add method of the SortedList class uses this interface to sort the objects as they are being added to the SortedList.

The algorithm that the Array.Sort and ArrayList.Sort methods use to sort an array's elements is the QuickSort algorithm.


IComparer is designed to solve the problem of allowing objects to be sorted based on different criteria in different contexts. This interface also allows us to sort types that we did not write. If we wanted to also be able to sort the Square objects by height, we could create a new class called CompareHeight, which would also implement the IComparer interface:

public class CompareHeight : IComparer
{
    public int Compare(object obj1, object obj2)
    {
        if (!(obj1 is Square) || !(obj2 is Square))
        {
            throw (new ArgumentException("Both parameters must be of type Square."));
        }
        else
        {
            Square square1 = (Square)obj1;
            Square square2 = (Square)obj2;

            if (square1.Height == square2.Height)
            {
                return (0);
            }
            else if (square1.Height > square2.Height)
            {
                return (1);
            }
            else if (square1.Height < square2.Height)
            {
                return (-1);
            }
            else
            {
                return (-1);
            }
        }
    }
}

This class is then passed in to the IComparer parameter of the Sort routine. Now we can specify different ways to sort our Square objects.

For best performance, keep the CompareTo method short and efficient, since it will be called multiple times by the Sort methods. For example, in sorting an array with 4 items, the Compare method was called 10 times.


The following method shows how to use the Square and CompareHeight structures with the Array, ArrayList, and SortedList classes:

public static void TestSort( )
{
    Square[] arrayOfSquares = new Square[4] {new Square(1,3), 
                                             new Square(4,3),
                                             new Square(2,1),
                                             new Square(6,1)};

    ArrayList arrayListOfSquares = new ArrayList( );
    arrayListOfSquares.Add(new Square(1,3));
    arrayListOfSquares.Add(new Square(4,3));
    arrayListOfSquares.Add(new Square(2,1));
    arrayListOfSquares.Add(new Square(6,1));

    IComparer HeightCompare = new CompareHeight( );

    // Test an ARRAY
    Console.WriteLine("ARRAY");
    Console.WriteLine("Original array");
    foreach (Square s in arrayOfSquares)
    {
        Console.WriteLine(s.ToString( ));
    }

    Console.WriteLine( );
    Console.WriteLine("Sorted array using IComparer=HeightCompare");
    Array.Sort(arrayOfSquares, HeightCompare);
    foreach (Square s in arrayOfSquares)
    {
        Console.WriteLine(s.ToString( ));
    }

    Console.WriteLine( );
    Console.WriteLine("Sorted array using IComparable");
    Array.Sort(arrayOfSquares);
    foreach (Square s in arrayOfSquares)
    {
        Console.WriteLine(s.ToString( ));
    }

    // Test an ARRAYLIST
    Console.WriteLine( );
    Console.WriteLine( );
    Console.WriteLine("ARRAYLIST");
    foreach (Square s in arrayListOfSquares)
    {
        Console.WriteLine(s.ToString( ));
    }

    Console.WriteLine( );
    Console.WriteLine("Sorted ArrayList using IComparer=HeightCompare");
    arrayListOfSquares.Sort(HeightCompare);
    foreach (Square s in arrayListOfSquares)
    {
        Console.WriteLine(s.ToString( ));
    }

    Console.WriteLine( );
    Console.WriteLine("Sorted ArrayList using IComparable");
    arrayListOfSquares.Sort( );
    foreach (Square s in arrayListOfSquares)
    {
        Console.WriteLine(s.ToString( ));
    }

    // Test a SORTEDLIST
    SortedList SortedListOfSquares = new SortedList( );
    SortedListOfSquares.Add(0, new Square(1,3));
    SortedListOfSquares.Add(2, new Square(4,3));
    SortedListOfSquares.Add(1, new Square(2,1));
    SortedListOfSquares.Add(3, new Square(6,1));

    Console.WriteLine( );
    Console.WriteLine( );
    Console.WriteLine("SORTEDLIST");
    foreach (DictionaryEntry s in SortedListOfSquares)
    {
        Console.WriteLine(s.Key + " : " + ((Square)s.Value).ToString( ));
    }    
}

This code displays the following output:

ARRAY
Original array
Height:1  Width:3
Height:4  Width:3
Height:2  Width:1
Height:6  Width:1

Sorted array using IComparer=HeightCompare
Height:1  Width:3
Height:2  Width:1
Height:4  Width:3
Height:6  Width:1

Sorted array using IComparable
Height:2  Width:1
Height:1  Width:3
Height:6  Width:1
Height:4  Width:3

ARRAYLIST
Height:1  Width:3
Height:4  Width:3
Height:2  Width:1
Height:6  Width:1

Sorted ArrayList using IComparer=HeightCompare
Height:1  Width:3
Height:2  Width:1
Height:4  Width:3
Height:6  Width:1

Sorted ArrayList using IComparable
Height:2  Width:1
Height:1  Width:3
Height:6  Width:1
Height:4  Width:3

SORTEDLIST
0 : Height:1  Width:3
1 : Height:2  Width:1
2 : Height:4  Width:3
3 : Height:6  Width:1

See Also

See Recipe 3.6; see the "IComparable Interface" topic in the MSDN documentation.

    [ Team LiB ] Previous Section Next Section