About Lukas Eder

Lukas is a Java and SQL enthusiast developer. He created the Data Geekery GmbH. He is the creator of jOOQ, a comprehensive SQL library for Java, and he is blogging mostly about these three topics: Java, SQL and jOOQ.

Java 8 Friday Goodies: Lambdas and Sorting

At Data Geekery, we love Java. And as we’re really into jOOQ’s fluent API and query DSL, we’re absolutely thrilled about what Java 8 will bring to our ecosystem. We have blogged a couple of times about some nice Java 8 goodies, and now we feel it’s time to start a new blog series, the…

Java 8 Friday

Every Friday, we’re showing you a couple of nice new tutorial-style Java 8 features, which take advantage of lambda expressions, extension methods, and other great stuff. You’ll find the source code on GitHub.

Java 8 Goodie: Lambdas and Sorting

Sorting arrays, and collections is an awesome use-case for Java 8′s lambda expression for the simple reason that Comparator has always been a @FunctionalInterface all along since its introduction in JDK 1.2. We can now supply Comparators in the form of a lambda expression to various sort() methods.

For the following examples, we’re going to use this simple Person class:

static class Person {
    final String firstName;
    final String lastName;

    Person(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    @Override
    public String toString() {
        return "Person{" +
                "firstName='" + firstName + '\'' +
                ", lastName='" + lastName + '\'' +
                '}';
    }
}

Obviously, we could add natural sorting to Person as well by letting it implement Comparable, but lets focus on external Comparators. Consider the following list of Person, whose names are generated with some online random name generator:

List<Person> people =
Arrays.asList(
    new Person("Jane", "Henderson"),
    new Person("Michael", "White"),
    new Person("Henry", "Brighton"),
    new Person("Hannah", "Plowman"),
    new Person("William", "Henderson")
);

We probably want to sort them by last name and then by first name.

Sorting with Java 7

A “classic” Java 7 example of such a Comparator is this:

people.sort(new Comparator<Person>() {
  @Override
  public int compare(Person o1, Person o2) {
    int result = o1.lastName.compareTo(o2.lastName);

    if (result == 0)
      result = o1.firstName.compareTo(o2.firstName);

    return result;
  }
});
people.forEach(System.out::println);

And the above would yield:

Person{firstName='Henry', lastName='Brighton'}
Person{firstName='Jane', lastName='Henderson'}
Person{firstName='William', lastName='Henderson'}
Person{firstName='Hannah', lastName='Plowman'}
Person{firstName='Michael', lastName='White'}

Sorting with Java 8

Now, let’s translate the above to equivalent Java 8 code:

Comparator<Person> c = (p, o) ->
    p.lastName.compareTo(o.lastName);

c = c.thenComparing((p, o) ->
    p.firstName.compareTo(o.firstName));

people.sort(c);
people.forEach(System.out::println);

The result is obviously the same. How to read the above? First, we assign a lambda expression to a local Person Comparator variable:

Comparator<Person> c = (p, o) ->
    p.lastName.compareTo(o.lastName);

Unlike Scala, C#, or Ceylon which know type inference from an expression towards a local variable declaration through a val keyword (or similar), Java performs type inference from a variable (or parameter, member) declaration towards an expression that is being assigned.

In other, more informal words, type inference is performed from “left to right”, not from “right to left”. This makes chaining Comparators a bit cumbersome, as the Java compiler cannot delay type inference for lambda expressions until you pass the comparator to the sort() method.

Once we have assigned a Comparator to a variable, however, we can fluently chain other comparators through thenComparing():

c = c.thenComparing((p, o) ->
    p.firstName.compareTo(o.firstName));

And finally, we pass it to the List‘s new sort() method, which is a default method implemented directly on the List interface:

default void sort(Comparator<? super E> c) {
    Collections.sort(this, c);
}

Workaround for the above limitation

While Java’s type inference “limitations” can turn out to be a bit frustrating, we can work around type inference by creating a generic IdentityComparator:

class Utils {
    static <E> Comparator<E> compare() {
        return (e1, e2) -> 0;
    }
}

With the above compare() method, we can write the following fluent comparator chain:

people.sort(
    Utils.<Person>compare()
         .thenComparing((p, o) -> 
              p.lastName.compareTo(o.lastName))
         .thenComparing((p, o) -> 
              p.firstName.compareTo(o.firstName))
);

people.forEach(System.out::println);

Extracting keys

This can get even better. Since we’re usually comparing the same POJO / DTO value from both Comparator arguments, we can provide them to the new APIs through a “key extractor” function. This is how it works:

people.sort(Utils.<Person>compare()
      .thenComparing(p -> p.lastName)
      .thenComparing(p -> p.firstName));
people.forEach(System.out::println);

So, given a Person p we provide the API with a function extracting, for instance, p.lastName. And in fact, once we use key extractors, we can omit our own utility method, as the libraries also have a comparing() method to initiate the whole chain:

people.sort(
    Comparator.comparing((Person p) -> p.lastName)
          .thenComparing(p -> p.firstName));
people.forEach(System.out::println);

Again, we need to help the compiler as it cannot infer all types, even if in principle, the sort() method would provide enough information in this case. To learn more about Java 8′s generalized type inference, see our previous blog post.

Conclusion

As with Java 5, the biggest improvements of the upgrade can be seen in the JDK libraries. When Java 5 brought typesafety to Comparators, Java 8 makes them easy to read and write (give or take the odd type inference quirk).

Java 8 is going to revolutionise the way we program, and next week, we will see how Java 8 impacts the way we interact with SQL.
 

Do you want to know how to develop your skillset to become a Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

JPA Mini Book

Learn how to leverage the power of JPA in order to create robust and flexible Java applications. With this Mini Book, you will get introduced to JPA and smoothly transition to more advanced concepts.

JVM Troubleshooting Guide

The Java virtual machine is really the foundation of any Java EE platform. Learn how to master it with this advanced guide!

Given email address is already subscribed, thank you!
Oops. Something went wrong. Please try again later.
Please provide a valid email address.
Thank you, your sign-up request was successful! Please check your e-mail inbox.
Please complete the CAPTCHA.
Please fill in the required fields.

2 Responses to "Java 8 Friday Goodies: Lambdas and Sorting"

  1. Awesome! Thanks for writing this up. I’m really looking forward to the release of Java 8. Glad to see that they also improved comparators!

    (Just one thing: I think your Java 7 soring example is a bit too modern, having “people.forEach(System.out::println);” at the end of it – Line 12)

Leave a Reply


+ three = 9



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy | Contact
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.
Do you want to know how to develop your skillset and become a ...
Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

Get ready to Rock!
You can download the complementary eBooks using the links below:
Close