How to implement Comparator and Comparable in Java with Lambda Expression & method reference? Example

Hello guys, After Java 8 it has become a lot easier to work with Comparator and Comparable classes in Java. You can implement a Comparator using lambda expression because it is a SAM type interface. It has just one abstract method compare() which means you can pass a lambda expression where a Comparator is expected. Many Java programmers often ask me, what is the best way to learn lambda expression of Java 8?  And, my answer is, of course by using it on your day to the day programming task. Since implementing equals(), hashcode(), compareTo(), and compare() methods are some of the most common tasks of a Java developer, it makes sense to learn how to use the lambda expression to implement Comparable and Comparator in Java.

Though, some of you might have a doubt that, can we use lambda expression with Comparator? because it's an old interface and may not implement functional interface annotated with @FunctionalInterface annotation?

The answer to that question is Yes, you can use a lambda expression to implement Comparator and Comparable interface in Java, and not just these two interfaces but to implement any interface, which has only one abstract method because those are known as SAM (Single Abstract Method) Type and lambda expression in Java supports that.

That's why lambda expression in Java 8 is also known as SAM type, where SAM stands for Single Abstract Method. Though, you should also remember that from Java 8 interface can have non-abstract methods as well as default and static methods.

This was one of the very intelligent decisions made by Java designers, which makes the lambdas even more useful. Because of this, you can use lambda expressions with Runnable, Callable, ActionListener, and several other existing interfaces from JDK API which has just one abstract method.

You should also check out these Java Functional Programming courses to learn more about why lambda expression was introduced in Java and the benefits of using lambdas in Java code, particularly on the Java Collection framework.






The Comparator is a Functional Interface in Java 8

By the way, you don't need to  worry in the case of Comparator, because it has been made to implement the @FunctionalInterface as shown below:


@FunctionalInterfaces
public interface Comparator<T> {
 ....
}

This code snippet is from JDK 1.8, if you are using Netbeans you can open this class by typing Ctrl+O and if you are using Eclipse you open this class by using the Open type shortcut Ctrl+Shift+T. See here for more useful Eclipse shortcuts for Java Programmers.

Even the Runnable interface is also annotated with @FunctionalInterface as seen below:

@FunctionalInterface
public interface Runnable {
   .......
}

but yes ActionListener is not annotated with @FunctionalInterface, but you can still use it in lambda expressions because it just got one abstract method called actionPerformed()

public interface ActionListener extends EventListener {

    /**
     * Invoked when an action occurs.
     */
    public void actionPerformed(ActionEvent e);

}


Earlier we have seen some hands-on examples of Java 8 Streams, here we will learn how to use lambda expression by implementing the Comparator interface in Java. This will make creating a custom Comparator very easy and reduce lots of boilerplate code.

By the way, the From Collections to Streams in Java 8 Using the Lambda Expression course only covers lambda expression and streams, it doesn't cover all other Java 8 features e.g. new Date and Time API, new JavaScript engine, and other small enhancements like Base64 encoder-decoder and performance improvements.

For other Java 8 changes, I suggest you check out these Java 8 tutorials and courses from Udemy and Pluralsight. It is a short and concise course but covers all major Java 8 features.


.


How to Implement Comparator using Lambda Expression

As I said before using lambdas to implement a Comparator is a good way to learn how lambda expression works in Java. Since lambda expression in Java is SAM type (Single Abstract Method) you can use it with any interface which got just one method like Comparator, Comparable, Runnable, Callable, ActionListener, and so on.

Earlier we used to use the Anonymous class to implement these one method interfaces, mostly when we want to pass them to a method or just want to use them locally like creating a thread for some temporary task or handling the event.

Now we can use a lambda expression to implement these methods, In these cases, lambdas work exactly like an anonymous class but without the heavy dose of boilerplate code required before as shown in the following diagram:


How to implement Comparator in Java 8 using lambdas

Anyway, here is our Java program to implement Comparator using the lambda expression in Java 8:


import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
 * How to sort Objects in Java 8, by implementing Comparator using lambda
 * expression.
 *
 * @author WINDOWS 8
 *
 */
public class ComparatorUsingLambdas{

    public static void main(String args[]) {

        // list of training courses, our target is to sort these courses
        // based upon their price or title
        List<TrainingCourses> onlineCourses = new ArrayList<>();
        onlineCourses.add(new TrainingCourses("Java", new BigDecimal("200")));
        onlineCourses.add(new TrainingCourses("Scala", new BigDecimal("300")));
        onlineCourses.add(new TrainingCourses("Spring", new BigDecimal("250")));
        onlineCourses.add(new TrainingCourses("NoSQL", new BigDecimal("310")));


        // Creating Comparator to compare Price of training courses
        final Comparator<TrainingCourses> PRICE_COMPARATOR 
         = new Comparator<TrainingCourses>() {
            @Override
            public int compare(TrainingCourses t1, TrainingCourses t2) {
                return t1.price().compareTo(t2.price());
            }
        };


        // Comparator to compare title of courses
        final Comparator<TrainingCourses> TITLE_COMPARATOR 
         = new Comparator<TrainingCourses>() {
            @Override
            public int compare(TrainingCourses c1, TrainingCourses c2) {
                return c1.title().compareTo(c2.title());
            }
        };


        // sorting objects using Comparator by price
        System.out.println("List of training courses, before sorting");
        System.out.println(onlineCourses);
        Collections.sort(onlineCourses, PRICE_COMPARATOR);
       
        System.out.println("After sorting by price, increasing order");
        System.out.println(onlineCourses);
        System.out.println("Sorting list by title ");      
       Collections.sort(onlineCourses, TITLE_COMPARATOR);
        System.out.println(onlineCourses);


        // Now let's see how less code you need to write if you use
        // lambda expression from Java 8, in place of anonymous class
        // we don't need an extra line to declare comparator, we can
        // provide them in place to sort() method.
       
 
        System.out.println("Sorting objects in decreasing order of price, using lambdas");
        Collections.sort(onlineCourses, (c1, c2) -> c2.price().compareTo(c1.price()));
        System.out.println(onlineCourses);
       
        System.out.println("Sorting list in decreasing order of title, using lambdas");
        Collections.sort(onlineCourses, (c1, c2) -> c2.title().compareTo(c1.title()));
        System.out.println(onlineCourses);
    }
}

class TrainingCourses {
    private final String title;
    private final BigDecimal price;

    public TrainingCourses(String title, BigDecimal price) {
        super();
        this.title = title;
        this.price = price;
    }

    public String title() {
        return title;
    }

    public BigDecimal price() {
        return price;
    }

    @Override
    public String toString() {
        return String.format("%s : %s", title, price);
    }
}

Output:
List of training courses, before sorting
[Java : 200, Scala : 300, Spring : 250, NoSQL : 310]
After sorting by price, increasing order
[Java : 200, Spring : 250, Scala : 300, NoSQL : 310]
Sorting list by title
[Java : 200, NoSQL : 310, Scala : 300, Spring : 250]
Sorting objects in decreasing order of price, using lambdas
[NoSQL : 310, Scala : 300, Spring : 250, Java : 200]
Sorting list in decreasing order of title, using lambdas
[Spring : 250, Scala : 300, NoSQL : 310, Java : 200]


In this example, we have an object called the TrainingCourse, which represents a typical training course from institutes. For simplicity, it just got two attributes title and price, where the title is String and price is BigDecimal because float and double are not good for exact calculations.

Now we have a list of training courses and our task is to sort based on their price or based upon their title. Ideally, TrainingCourse class should implement the Comparable interface and sort training courses by their title, i.e. their natural order.

Anyway, we are not doing that here to focus purely on Comparator.

To complete these tasks we need to create two custom Comparator implementations, one to sort TrainingCourse by title and the other to sort it by price.

To show the stark difference in the number of lines of code you need to do this prior to Java 8 and in JDK 1.8, I have implemented that two Comparator first using Anonymous class and later using the lambda expression.

You can see that by using lambdas implementing Comparator just take one line and you can even do that on method invocation, without sacrificing readability.

This is the main reason, why you should use the lambda expression to implement ComparatorRunnableCallable, or ActionListener post-Java 8, they make your code more readable and terse.

For a complete Java 8 learning, I recommend The Complete Java MasterClass course on Udemy. It is also the most up-to-date course to learn Java.






Implement Comparator using Method References in Java 8

By the way, you can even do better by leveraging new methods added on the Comparator interface in Java 8 and by using method references as shown below:

Java 8 Comparator example

You can see that by using new methods in Comparator like comparing()  and method references, you can implement a Comparator in just one line after Java 8 version. I strongly recommend this style of code in the current Java word.


That's all on how to implement a Comparator using Java 8 lambda expression. You can see it take very little code to create a custom Comparator using lambdas than an anonymous class. From Java 8 there is no point using anonymous class anymore, in fact, use lambdas wherever you used to use Anonymous class. 

Make sure you implement SAM interfaces using lambdas like Runnable, Callable, ActionListener, etc. If you want to learn Java 8, then you can also refer to the following resources:


Related Java 8 Tutorials

If you are interested in learning more about new features of Java 8 and Java in general, here are my earlier articles covering some of the important concepts of Java 8
  • 20 Examples of Date and Time in Java 8 (tutorial)
  • What is the default method in Java 8? (example)
  • How to use filter() method in Java 8 (tutorial)
  • Top 5 Courses to learn Lambdas and Stream in Java (courses)
  • How to sort the map by keys in Java 8? (example)
  • Top 5 Courses to become a full-stack Java developer (courses)
  • 5 Books to Learn Java 8 from Scratch (books)
  • How to use Stream class in Java 8 (tutorial)
  • Top 5 Courses to learn Functional Programming in Java (courses)
  • How to convert List to Map in Java 8 (solution)
  • How to join String in Java 8 (example)
  • Difference between abstract class and interface in Java 8? (answer)
  • 10 Free Courses for Experienced Java Programmers (courses)
  • How to use peek() method in Java 8 (example)
  • How to sort the may by values in Java 8? (example)
  • How to format/parse the date with LocalDateTime in Java 8? (tutorial)
  • 5 Free Courses to learn Java 8 and 9 (courses)

Thanks for reading this article so far. If you like this article then please share it with your friends and colleagues. If you have any questions or suggestions then please drop a note.

P. S. - If you are serious about learning Java 8 Functional programming but looking for a free online training course to start with then you can also check out these free Java online courses on Udemy. It's completely free and you just need a Udemy account to join this course.

9 comments:

  1. You can do even better:

    Collections.sort(onlineCourses, Comparator.comparing(TrainingCourse::title).reversed())

    ReplyDelete
    Replies
    1. Wow, the great Cay Horstmann.. I am honored with your visit to my humble blog. Thanks for dropping by and indeed method reference can make the code even more succinct.

      Delete
  2. Java 8 rocks!! not just Comparator but any other class which has single abstract method can be re-written using lambda expression and method references e.g. ActionListener, Runnable and Callable interfaces.

    ReplyDelete
  3. I tried implementing Comparable by using Lambda Expression, but no luck as it is using state of the object in comparison and not just the parameter object. It would be great if you provide an example to implement Comparable interface by using Lambda Expression.

    ReplyDelete
  4. Nice write up, the frustrating thing about all the great material on your blog is that when you try to copy paste the examples into eclipse, the code samples always come up as single line, take this as constructive feed back, but it sucks having to type the entire example, sometimes large number of lines of code.

    ReplyDelete
    Replies
    1. @Anonymous, yes, that's a nightmare, I'll try to solve that, thanks for feedback.

      Delete
  5. I see thenComparing method, but no compose method for Comparator. Any clarification on it?

    ReplyDelete
  6. While printing you have used compareTo() but you have actually created lambda expression of compare() method of Comparator interface. You haven't created lambda for compareTo()

    ReplyDelete

Feel free to comment, ask questions if you have any doubt.