Generic lists were introduced in .NET 2.0, and I have been a big fan of them ever since. I used to use the ArrayList class, before .NET 2.0, but the List<T> has the major advantage of being strongly typed, which is cool because the compiler will let me know if I’m trying to add another type than the one specified. When .NET is aware of which type of objects are in the list, everything becomes much faster too, because no boxing is required when adding and pulling items from the list.
This post is about sorting your generic lists, mainly because I do it all the time, and it’s actually quite easy, once you understand how it works. The Sort() method on a generic list has 4 overloads, and since the first one doesn’t take any parameters at all, it’s by far the easiest one to use
. Here is an example:
List<string> list = new List<string>();
list.Add("Tom Hanks");
list.Add("Al Pacino");
list.Add("Mel Gibson");
list.Sort();
foreach(string name in list)
Console.WriteLine(name);
We simply create a new list, add some names to it, call the Sort() method and then we output the list to the console. If you try the example, you will see that the items get sorted just perfectly – but how? Because the String object has a CompareTo() method that will do all the work, and since the compiler knows that the list will contain strings only, it can safely compare each item using the CompareTo() method of the string class. You can try changing the list type to a List<int> and add numbers to it instead – it will work like a charm! However, this was a simple example. In a lot of situations, you will be using the List<T> to store your own types in, and .NET won’t necessarily know how to sort them. Look at this example:
public class Person
{
private string firstName;
private string lastName;
public Person(string firstName, string lastName)
{
this.firstName = firstName;
this.lastName = lastName;
}
public string FirstName
{
get { return firstName; }
set { firstName = value; }
}
public string LastName
{
get { return lastName; }
set { lastName = value; }
}
public override string ToString()
{
return this.firstName + " " + this.lastName;
}
}
If you create a new List<Person> and add some persons to it and try to call the Sort() method, you will get the following error:
Failed to compare two elements in the array.
Why? Because .NET has no idea how you want this new and strange type sorted. Fortunately, it’s easy to fix. I usually prefer doing it by letting my classes implement the IComparable interface:
public class Person : IComparable
{
...
public int CompareTo(object obj)
{
if(obj is Person)
return this.LastName.CompareTo(((Person)obj).LastName);
else
return 0;
}
...
}
The CompareTo method is rather simple: It returns 0 if the two compared items are identical, and -1 or +1, based on which item is considered “bigger”. In this case, we simply take advantage of the fact that the String class has a CompareTo method, and then return the result of comparing the current object’s LastName property to the LastName property of the object passed in through the argument (obj). Try running the code now, and you should see the list get sorted by the persons last names. Now, if you wish to sort in the reverse order, you simply use the CompareTo() method on the obj instead of on this:
return ((Person)obj).LastName.CompareTo(this.LastName);
If you don’t want to, or can’t, implement the IComparable interface on the target class, you can do it “on the fly” by passing a delegate to the Sort() method. Here is a complete example:
List<Person> list = new List<Person>();
list.Add(new Person("Tom", "Hanks"));
list.Add(new Person("Al", "Pacino"));
list.Add(new Person("Mel", "Gibson"));
list.Sort(delegate(Person p1, Person p2) { return p1.FirstName.CompareTo(p2.FirstName); });
foreach(Person p in list)
Console.WriteLine(p.ToString());
It might seem a bit faster and simpler, but it does require you to either write this delegate each time you use it, or declare it somewhere and pass it to the Sort method. Letting the objects them self do the comparing is probably a more clean solution, but not always possible. Hopefully this covers all your sorting needs