Sorting Lists in C# with IComparable and IComparer

Collections can be sorted using a variety of techniques. I'm giong to go over a few of the techniques for implementing sorting with Arrays. This is meant as a reference.

For starters, you can use System.Collections.ArrayList or System.Collections.Generic.List<T> to store information in an expandable array. The ArrayList is not strongly typed and requires that you cast the values that are stored in it to their appropriate types. List<T> is generic and allows you to add and access the values via their type.

The main focus of this article is on the Sort method that is available in both the ArrayList and List classes. Objects stored in the List will be sortable if they implement the IComparable interface. For default types, such as Integers and Strings, the array will automatically sort because they implement IComparable. If you use a custom class such as Person, you will need to do one or more of the following:

1) Implement IComparable in the Person class
2) Create a Class that implements IComparer and pass an instance of it into Sort
3) Create a static method that matches the delegate pattern for IComparer's Compare method and pass it into Sort

These options allow you to build an extraordinarily extensible way for you to sort your Lists.

For starters, you can make your class implement IComparable. IComparable comes in two varieties, non-generic and generic for use with ArrayList and List<T> respectively. The non-generic form shown below requires you cast the object to its appropriate type for comparison:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace _4Practice1
{
    class Program
    {
        static void Main(string[] args)
        {
            ArrayList list = new ArrayList();
            list.Add(new Person("Brian", "Mancini", 28));
            list.Add(new Person("Kenny", "Huang", 20));
            list.Add(new Person("Rob", "Lofgren", 38));

            list.Sort();

            foreach (Person p in list)
            {
                Console.WriteLine(p.LastName + ", " + p.FirstName);
            }

            Console.ReadKey();
        }
    }

    public class Person : IComparable
    {
        public string FirstName;
        public string LastName;
        public int Age;

        public Person(string firstname, string lastname, int age)
        {
            this.FirstName = firstname;
            this.LastName = lastname;
            this.Age = age;
        }

        public int CompareTo(object obj)
        {
            Person otherPerson = (Person)obj;
            return this.LastName.CompareTo(otherPerson.LastName);
        }
    }
}

If you are using the List<T> generic collection, you can save yourself time casting and a few lines of code by doing the following:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace _4Practice1
{
    class Program
    {
        static void Main(string[] args)
        {
            List<Person> list = new List<Person>();
            list.Add(new Person("Brian", "Mancini", 28));
            list.Add(new Person("Kenny", "Huang", 20));
            list.Add(new Person("Rob", "Lofgren", 38));

            list.Sort();

            foreach (Person p in list)
            {
                Console.WriteLine(p.LastName + ", " + p.FirstName);
            }

            Console.ReadKey();
        }
    }

    public class Person : IComparable<Person>
    {
        public string FirstName;
        public string LastName;
        public int Age;

        public Person(string firstname, string lastname, int age)
        {
            this.FirstName = firstname;
            this.LastName = lastname;
            this.Age = age;
        }

        public int CompareTo(Person other)
        {
            return this.LastName.CompareTo(other.LastName);
        }

    }
}

The second technique is to implement a comparer class to do the comparison for you. You create a class that Implements IComparer or IComparer<T> depending if you are using ArrayList or List<T>. The major downside is that you need to create additional classes for each Comparer, which can greatly clutter your code. The upside is that your List class does not need to implement IComparable since this is offloaded to the Comparer. Below is an example for an IComparer<Person> implementation that sorts by last name:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace _4Practice1
{
    class Program
    {
        static void Main(string[] args)
        {
            List<Person> list = new List<Person>();
            list.Add(new Person("Brian", "Mancini", 28));
            list.Add(new Person("Kenny", "Huang", 20));
            list.Add(new Person("Rob", "Lofgren", 38));

            list.Sort(new SortByLastName());

            foreach (Person p in list)
            {
                Console.WriteLine(p.LastName + ", " + p.FirstName);
            }

            Console.ReadKey();
        }
    }

    public class Person
    {
        public string FirstName;
        public string LastName;
        public int Age;

        public Person(string firstname, string lastname, int age)
        {
            this.FirstName = firstname;
            this.LastName = lastname;
            this.Age = age;
        }
    }

    public class SortByLastName : IComparer<Person>
    {        
        public int Compare(Person x, Person y)
        {
            return x.LastName.CompareTo(y.LastName);
        }        
    }
}

Finally, the newest and cleanest way to implement Comparers, IMO, is through the use of delegates. You can directly pass in a static method as a delegate to the Sort method. You can create any number of these methods inside your class for a nice clean implementation. Below is an implementation that contains three IComparer methods for sorting by LastName, FirstName, and Age.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace _4Practice1
{
    class Program
    {
        static void Main(string[] args)
        {
            List<Person> list = new List<Person>();
            list.Add(new Person("Brian", "Mancini", 28));
            list.Add(new Person("Kenny", "Huang", 20));
            list.Add(new Person("Rob", "Lofgren", 38));

            list.Sort(Person.CompareByAge);

            foreach (Person p in list)
            {
                Console.WriteLine(p.LastName + ", " + p.FirstName);
            }

            Console.ReadKey();
        }
    }

    public class Person
    {
        public string FirstName;
        public string LastName;
        public int Age;

        public Person(string firstname, string lastname, int age)
        {
            this.FirstName = firstname;
            this.LastName = lastname;
            this.Age = age;
        }

        public static int CompareByFirstName(Person x, Person y)
        {
            return x.FirstName.CompareTo(y.FirstName);
        }

        public static int CompareByLastName(Person x, Person y)
        {
            return x.LastName.CompareTo(y.LastName);
        }

        public static int CompareByAge(Person x, Person y)
        {
            return x.Age.CompareTo(y.Age);
        }
    }  
}

Comments

#1 Anonymous

thank you, very good example to sort List<> with delegates !

Recent comments