If you’re using Java 8 (or higher), you really should take advantage of lambda expressions in your sorting logic.

Why? Because it’s a great way to tighten your code and make it easier to read.

In this article we’ll go over how to use the Comparator interface with lambda expressions. As is usually the case, you’re welcome to browse the complete code on GitHub.

 

The Use Case

The use case here is that you’re running an e-commerce site that sells fishing tackle. You want to be able to sort your customers by name and age.

To that end, you have a Customer class. It’s a Java bean that contains exactly the fields you’d expect to see.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public class Customer {
 
    private Long id;
    private String firstName;
    private String lastName;
    private Integer age;
 
     
    public Customer(Long id, String firstName, String lastName, Integer age) {
        this.id = id;
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
    }
     
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getFirstName() {
        return firstName;
    }
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
     
    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append(lastName);
        builder.append(", ");
        builder.append(firstName);
        builder.append(" Age: ");
        builder.append(age);
        builder.append(" [ID=");
        builder.append(id);
        builder.append("]");
         
        return builder.toString();
    }
}

There’s nothing to complicated there. It’s just 4 fields with getters and setters. It also has a constructor and overrides toString().

 

The Classic Way

When you want to handle sorting in Java, you have one of two options: implement a Comparator or override the compareTo() method in a Java class that implements Comparable.

Obviously, we’re going to look at the first case here.

The “old school” way of using a Comparator was to typically instantiate it as an anonymous class. That works, but let’s face it: anonymous classes make the code look ugly.

Consider the following:

1
2
3
4
5
6
Comparator<Customer> byLastName = new Comparator<Customer>() {
    @Override
    public int compare(Customer c1, Customer c2) {
        return c1.getLastName().compareTo(c2.getLastName());
    }
};

Yeah, that works. But it’s a bit awkward to read.

Pile a bunch of those anonymous classes on top of each other and the code becomes even more complicated to parse with a visual scan.

On top of that, you still have to do the sorting with Collections.sort().

1
2
3
4
5
6
Collections.sort(listDevs, new Comparator<Developer>() {
    @Override
    public int compare(Developer o1, Developer o2) {
        return c1.getLastName().compareTo(c2.getLastName());
    }
});

Fairly ugly.

Also, you have to create a whole new class every time you want to change the sort. For example, if you want to sort by age instead of by last name, you’ll need a second block of code similar to what you see above.

Nah.

Lambda expressions will tighten your code considerably and make it easy to read.

 

Doing It With Lambda

Lambda expressions enable you to represent a method interface with an expression. They’re great for getting rid of anonymous inner classes, which is exactly what you’re going to see here.

Here’s an example of a lambda expression that instantiates a Comparator object:

1
2
Comparator<Customer> lastNameLambdaComparator =
    (c1, c2)->c1.getLastName().compareTo(c2.getLastName());

Well that’s a bit cleaner, isn’t it?

As you can see, lambda expressions rely on type safety. That’s why the code above doesn’t even define c1 and c2 in that first set of parentheses.

Note: you can define c1 and c2, but it’s unnecessary.

That parenthetical, incidentally, is a shorthand way of referring to the compare() method that you override when doing sorting the old-fashioned way.

The arrow expression just points to the code that does the work. In this case, it’s exactly the same as the main body of the compare() method in the previous block of code.

 

The Sorter Class

Now that you understand the requirements and you know how to implement a Comparator with a lambda expression, create a class that handles sorting by age and last name (ascending).

For each sort type, create two different sort methods: one that sorts the classic way and one that sorts with lambda expressions.

When everything is said and done, you should have a class that looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class Sorter {
 
    //comparator for classic sorting by last name
    private class LastNameClassicComparator implements Comparator<Customer> {
        @Override
        public int compare(Customer c1, Customer c2) {
            return c1.getLastName().compareTo(c2.getLastName());
        }       
    }
 
    //comparator for classic sorting by age
    private class AgeClassicComparator implements Comparator<Customer> {
        @Override
        public int compare(Customer c1, Customer c2) {
            return c1.getAge().compareTo(c2.getAge());
        }       
    }
     
    //comparator for lambda sorting by last name
    private Comparator<Customer> lastNameLambdaComparator =
            (c1, c2)->c1.getLastName().compareTo(c2.getLastName());
             
    //comparator for lambda sorting by age
    private Comparator<Customer> ageLambdaComparator =
            (c1, c2)->c1.getAge().compareTo(c2.getAge());
             
             
    public void sortByLastNameClassic(List<Customer> customers) {
        Collections.sort(customers, new LastNameClassicComparator());
    }
 
     
    public void sortByLastNameLambda(List<Customer> customers) {
        customers.sort(lastNameLambdaComparator);
    }
     
     
    public void sortByAgeClassic(List<Customer> customers) {
        Collections.sort(customers, new AgeClassicComparator());
    }
 
     
    public void sortByAgeLambda(List<Customer> customers) {
        customers.sort(ageLambdaComparator);
    }
}

The first two inner classes at the top implement a Comparator. The top one is used to sort by last name and the second one sorts by age.

The next two lines of code instantiate a Comparator object that handles sorting with lambda expressions.

There are 4 methods defined as well: two that handle sorting by age and two that handle sorting by last name.

 

Checking Your Work

Finally, create a Java application that uses the Sorter class to sort a number of customers. Here’s what that class should look like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class SortCustomers {
 
    public static void main(String[] args) {
        //instantiate the sorter object
        Sorter sorter = new Sorter();
         
        //get our out-of-order customers
        List<Customer> customers = getCustomers();
         
        System.out.println("Order before sorting");
        for (Customer customer : customers) {
            System.out.println(customer);
        }
        System.out.println("\n");
         
        //use the sort method you want
        sorter.sortByAgeClassic(customers);
        //sorter.sortByAgeLambda(customers);
        //sorter.sortByLastNameClassic(customers);
        //sorter.sortByLastNameLambda(customers);
         
        System.out.println("Order after sorting");
        for (Customer customer : customers) {
            System.out.println(customer);
        }
        System.out.println("\n\n\n");
    }
     
     
    private static List<Customer> getCustomers() {
        List<Customer> customers = new ArrayList<Customer>();
         
        customers.add(new Customer(1000l,"John","Smith",25));
        customers.add(new Customer(1001l,"Jane","Doe",36));
        customers.add(new Customer(1002l,"Jerry","Tyne",20));
        customers.add(new Customer(1003l,"Glenn","First",29));
        customers.add(new Customer(1004l,"Beth","Abbey",35));
 
        return customers;
    }
}

The first thing the application does is instantiate the aforementioned Sorter class.

Next, the code grabs a List of Customer objects. That List is created on the fly here within the same class.

The following code block just prints out the list of customers in unsorted order. That way, you can see that the customers aren’t sorted by default.

Then, the code invokes one of the four sorting methods in the Sorter class. It’s up to you which one you want to call, but it’s a great idea to test all 4 of them.

After that, the code just prints out the customers in sorted order. If you sorted by age, you should see something like this in the output:

Order before sorting
Smith, John Age: 25 [ID=1000]
Doe, Jane Age: 36 [ID=1001]
Tyne, Jerry Age: 20 [ID=1002]
First, Glenn Age: 29 [ID=1003]
Abbey, Beth Age: 35 [ID=1004]
 

 

Order after sorting
Tyne, Jerry Age: 20 [ID=1002]
Smith, John Age: 25 [ID=1000]
First, Glenn Age: 29 [ID=1003]
Abbey, Beth Age: 35 [ID=1004]
Doe, Jane Age: 36 [ID=1001]

 

Wrapping It Up

Lambda expressions are a great way to shrink your code and make it more readable. If you’re still doing things the old way, consider refactoring your code today.

Also, feel free to check out the complete code for this tutorial on GitHub. You can pull it down in Eclipse and run the Java application right out of the box.

Have fun!