Sorting by IComparable vs IComparer

29 Jun

These are two interfaces that used to confuse me. I didn’t know when to use one or when to use the other. The aim of this post is to make it easier for someone to understand the differences and choose appropriately.

In as a few words as possible:
i)A class that implements IComparable can be sorted or compared to another class instance in a manner that you define.

ii)A class that implements IComparer can be used to sort or compare classes that do or do not implement IComparable.

The List.Sort() Method.

Every list(generic list, array, arraylist) has a built-in method called Sort(). The Sort() method knows how to sort all objects that implement the IComparable interface. The method uses an object’s CompareTo() method to compare it with other objects and use its return value to figure out which object comes first.

To sort objects that do not implement IComparable, you have the IComparer Interface. It’s used to assist the list’s Sort() method in sorting its members.

IComparable.
You implement this within the class you want to be sorted. The interface has only one method called CompareTo() that takes an object as a parameter, compares it with the class and returns an integer value that figures out which object comes first.

See the sample class below.

Public Class Person
    Implements IComparable(Of Person)
    'these should be properties
    Public FirstName As String
    Public LastName As String
    Public Age As Integer

    Public Sub New(ByVal first As String, ByVal last As String, ByVal age As Integer)
        Me.FirstName = first
        Me.LastName = last
        Me.Age = age
    End Sub

    Public Function fullName() As String
        Return Me.FirstName & " " & Me.LastName & ": Age - " & Me.Age
    End Function

    Public Function CompareTo(ByVal other As Person) As Integer Implements System.IComparable(Of Person).CompareTo
        If Me.Age > other.Age Then
            Return 1
        ElseIf Me.Age < other.Age Then
            Return -1
        Else
            Return 0
        End If
    End Function
End Class

Notice the CompareTo() method. It takes another Person class as a parameter, compares the Ages and returns a number indicating
which one comes first. In this method you can create any sorting logic that you need.
You could for example compare by Age, FirstName or Lastname. In this sample the classes will be sorted by Age.

Implementation:
One you have a list of objects that implement IComparable, all you have to do to sort them is just call the List.Sort() method without
passing any parameters. See sample code for a button bellow.

Dim list As List(Of Person)

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        list = New List(Of Person)
        list.Add(New Person("Nick", "Masao", 27))
        list.Add(New Person("Sydney", "Mtaita", 3))
        list.Add(New Person("Ruth", "Mtaita", 23))

        Debug.WriteLine("")
        Debug.WriteLine("Unsorted:")
        For Each p As Person In list
            Debug.WriteLine(p.fullName)
        Next

'Just call the sort method without passing any parameters and you are done
‘The Sort() method uses the logic in the CompareTo() method in the class to ‘sort the list members

        list.Sort()

        Debug.WriteLine("")
        Debug.WriteLine("Sorted by Age:")
        For Each p As Person In list
            Debug.WriteLine(p.fullName)
        Next
End Sub

The code produces the following output.
IComparable_Output
As you can see the objects were sorted by age.

IComparer.

Implement this in a class that you will use to sort other classes that may or may not implement IComparable. This class will be a ‘comparer’ and will impose the sorting order in the List.Sort() method. The interface has only one method called Compare() that takes two objects and returns a value which determines which object comes first.

To sort a list using IComparer, you need to create a new instance of the class that implements it and pass that instance to the List.Sort() method. The list object will then use the class’s Compare() method to sort the members.

Say for example you have a person class with the following definition.  Note that it doesn’t implement any interface.

Public Class Person
    'these should be properties
    Public FirstName As String
    Public LastName As String
    Public Age As Integer

    Public Sub New(ByVal first As String, ByVal last As String, ByVal age As Integer)
        Me.FirstName = first
        Me.LastName = last
        Me.Age = age
    End Sub

    Public Function fullName() As String
        Return Me.FirstName & " " & Me.LastName & ": Age - " & Me.Age
    End Function
End Class

Now to use IComparer to sort a list of objects like the above, you need to create a simple class that implements IComparer and define the sorting manner, like so.

Public Class PersonComparer_ByAge
    Implements IComparer(Of Person)

    Public Function Compare(ByVal x As Person, ByVal y As Person) As Integer Implements System.Collections.Generic.IComparer(Of Person).Compare
        If x.Age > y.Age Then
            Return 1
        ElseIf x.Age < y.Age Then
            Return -1
        Else
            Return 0
        End If
    End Function
End Class

The Class sorts the Person objects by Age. You could also sort by FirstName or LastName. The only limitation of
IComparer is that it has access to public members of the class only.

See another class that implements IComparer but this time sorts by the FirstName field.

Public Class PersonComparer_ByFistName
    Implements IComparer(Of Person)

    Public Function Compare(ByVal x As Person, ByVal y As Person) As Integer Implements System.Collections.Generic.IComparer(Of Person).Compare
        If x.FirstName > y.FirstName Then
            Return 1
        ElseIf x.FirstName < y.FirstName Then
            Return -1
        Else
            Return 0
        End If
    End Function
End Class

Implementation:
If you have a list of objects, and you have a class that implements IComparer and has the sorting criteria like
the above class then you have to do the following to sort your list.

  1. Create an instance of your sorting class.
  2. Pass that instance to the list.sort() method.
  3. That’s it.

Sample button code that implements the code above.

Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        list = New List(Of Person)
        list.Add(New Person("Nick", "Masao", 27))
        list.Add(New Person("Sydney", "Mtaita", 3))
        list.Add(New Person("Ruth", "Mtaita", 23))

        Debug.WriteLine("Unsorted:")
        For Each p As Person In list
            Debug.WriteLine(p.fullName)
        Next

        Debug.WriteLine("")

'instantiate the sorter/comparer class that implements IComparer

        Dim sortName As New PersonComparer_ByFistName

'pass it as criteria to the sort method

        list.Sort(sortName)

        Debug.WriteLine("Sorted by Firstname:")
        For Each p As Person In list
            Debug.WriteLine(p.fullName)
        Next

        Debug.WriteLine("")
'instantiate the comparer class and pass it to the sort method
        Dim sortAge As New PersonComparer_ByAge
        list.Sort(sortAge)

        Debug.WriteLine("Sorted by Age:")
        For Each p As Person In list
            Debug.WriteLine(p.fullName)
        Next
    End Sub

The above code produces the following output.

IComparer_Output

Advantages & Limitations.

The advantage of IComparable is that you have access to the private members. i.e you can create your sorting criteria using private members in the class since the CompareTo() method is withing the class. The limitation is you only have that one way of sorting.

The limitation of IComparer is that the object that implements it doesn’t have access to private members of the objects it sorts. It only has access to public members.

Its advantages are, as seen in the sample code above, you can create a number of sorting options, and you have the option of passing the more specific and more appropriate sorting criteria to the Sort() method.

This post is getting too long  and I think I’ll end it here. Hopefully it was an eye opened of some sorts. Till next time, yours truly.

About these ads

4 Responses to “Sorting by IComparable vs IComparer”

  1. vbwyrde November 25, 2009 at 2:49 am #

    This is very helpful. Thank you!

    What I’d like is to create my classes so that I can sort them by any property, and even multiple properties if necessary, such as by ObjectDate, ObjectTime, ObjectName. I’d like the method to be as generic as possible. Currently I create the class, and then create a collection that inherits the class:

    Public Class MyClass_Collection
    Inherits Collection(Of MyClass)
    End Class

    What I’d like is something to the effect of:

    MyClass.Sort(MyClass.Date, MyClass.Time, MyClass.Name)

    Result:

    1/1/2009, 1:00pm, Allen
    1/1/2009, 1:00pm, Betty
    1/1/2009, 1:01pm, Allen
    1/2/2009, 1:00pm, Allen

    What’s the best way to get that effect? Thanks again!
    :)
    Mark

  2. vbwyrde November 25, 2009 at 2:52 am #

    Please when you respond to my above question about how to create a generic class sort capability, please respond to this comment. I forgot to click the Notify By Email on the one above, and I’d very much like to be notified of the response. Thanks!

    Mark

  3. Tijl Vandersteene October 29, 2012 at 7:48 pm #

    Many thanks.
    I have been struggling with the concept and use of these two interfaces for much too long. Finally my struggle has come to an end.
    I’m very grateful to you for this post and your willingness to share knowledge in such a clear way.

  4. dotnet December 28, 2012 at 4:21 am #

    This article really clears up the confusion I have between the two. You did a fantastic job on the explanation. Thank you so much.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: