Introduction to the

In this tutorial, we’ll first look at Lambda support in Java 8, and specifically how to use it to write comparators and sort collections.

First, let’s define a simple entity class:

public class Human {
    private String name;
    private int age;
}
Copy the code

A simple sort of List

Prior to Java 8, sorting a collection would involve creating anonymous inner classes for the comparators used in sorting:

new Comparator<Human>() {
    @Override
    public int compare(Human h1, Human h2) {
        returnh1.getName().compareTo(h2.getName()); }}Copy the code

This one is easier. Let me look at the unit test example:

@Test
public void givenPreLambda() {
    List<Human> humans = Lists.newArrayList(
      new Human("Sarah", 10), 
      new Human("Jack", 12)); Collections.sort(humans, new Comparator<Human>() { @Override public int compare(Human h1, Human h2) {returnh1.getName().compareTo(h2.getName()); }}); Assert.assertThat(humans.get(0), equalTo(new Human("Jack", 12)));
}
Copy the code

Use of Lambda in sorting

With the introduction of Lambdas, we can now bypass anonymous inner classes and achieve the same result with a simple, functional semantic implementation:

(final Human h1, final Human h2) -> h1.getName().compareTo(h2.getName());
Copy the code

Again, we can use the previous test case:

@Test
public void test() {
    List<Human> humans = Lists.newArrayList(
      new Human("Sarah", 10), 
      new Human("Jack", 12)); humans.sort( (Human h1, Human h2) -> h1.getName().compareTo(h2.getName())); assertThat(humans.get(0), equalTo(new Human("Jack", 12)));
}
Copy the code

Note that we also used the new sorting API for java.util.List added to Java 8, rather than the old collections.sort API.

Sort without type definition

We can further simplify the expression by not specifying a type definition – the compiler can infer this for itself:

(h1, h2) -> h1.getName().compareTo(h2.getName())
Copy the code

For example:

@Test
public void test() {
     
    List<Human> humans = Lists.newArrayList(
      new Human("Sarah", 10), 
      new Human("Jack", 12)); humans.sort((h1, h2) -> h1.getName().compareTo(h2.getName())); assertThat(humans.get(0), equalTo(new Human("Jack", 12)));
}

Copy the code

This benefits from Lambda’s method support, which makes my code much cleaner.

Sort using static methods

Next, we will use Lambda Expression to perform the sort and reference static methods.

First, we define the compareByNameThenAge method to return exactly the same value as the compare method in the Comparator

object:

public static int compareByNameThenAge(Human lhs, Human rhs) {
    if (lhs.name.equals(rhs.name)) {
        return lhs.age - rhs.age;
    } else {
        returnlhs.name.compareTo(rhs.name); }}Copy the code

Now, how do we use it

humans.sort(Human::compareByNameThenAge);
Copy the code

Take a look at unit tests

@Test
public void test() {
     
    List<Human> humans = Lists.newArrayList(
      new Human("Sarah", 10), 
      new Human("Jack", 12)); humans.sort(Human::compareByNameThenAge); Assert.assertThat(humans.get(0), equalTo(new Human("Jack", 12)));
}
Copy the code

Sort the internal API

We can also sort comparisons by using a combination of the Collections reference and the Comparator.comparing methods.

We’ll use getName() to build the Lambda expression and sort the List by name:

@Test
public void test() {
     
    List<Human> humans = Lists.newArrayList(
      new Human("Sarah", 10), 
      new Human("Jack", 12)); Collections.sort( humans, Comparator.comparing(Human::getName)); assertThat(humans.get(0), equalTo(new Human("Jack", 12)));
}
Copy the code

The reverse order

Java 8 also introduces a helper method for reversing the comparator, which we can quickly use to reverse our sort:

@Test
public void test() {
    List<Human> humans = Lists.newArrayList(
      new Human("Sarah", 10), 
      new Human("Jack", 12)); Comparator<Human> comparator = (h1, h2) -> h1.getName().compareTo(h2.getName()); humans.sort(comparator.reversed()); Assert.assertThat(humans.get(0), equalTo(new Human("Sarah", 10)));
}
Copy the code

Multiconditional sort

Comparing lambda expressions is not necessarily very simple, and we can write more complex expressions. For example, sort comparison by name and age.

@Test
public void test() {
    List<Human> humans = Lists.newArrayList(
      new Human("Sarah", 12), 
      new Human("Sarah", 10), 
      new Human("Zack", 12)); humans.sort((lhs, rhs) -> {if (lhs.getName().equals(rhs.getName())) {
            return lhs.getAge() - rhs.getAge();
        } else {
            returnlhs.getName().compareTo(rhs.getName()); }}); Assert.assertThat(humans.get(0), equalTo(new Human("Sarah", 10)));
}
Copy the code

Multi – condition combination sort

The same example can be implemented with new combinatorial support for comparators.

Starting with JDK 8, we can now combine multiple comparators to build more complex comparison logic:

@Test
public void test() {
     
    List<Human> humans = Lists.newArrayList(
      new Human("Sarah", 12), 
      new Human("Sarah", 10), 
      new Human("Zack", 12)); humans.sort( Comparator.comparing(Human::getName).thenComparing(Human::getAge) ); Assert.assertThat(humans.get(0), equalTo(new Human("Sarah", 10)));
}
Copy the code

The Stream order

We can also sort collections using Java 8’s Stream sorted() API.

We can sort streams using natural sort and the sort provided by the comparator. To do this, we have sorted(), which has two corresponding apis:

  • Sorted (); To use sorting to sort the elements of a Stream, the element class must implement the Comparable interface
  • sorted(Comparator
    comparator); Sort elements according to the Comparator instance

Let’s look at an example of how to use the sorted() method of natural sorting:

@Test
public final void test() {
    List<String> letters = Lists.newArrayList("B"."A"."C");
     
    List<String> sortedLetters = letters.stream().sorted().collect(Collectors.toList());
    assertThat(sortedLetters.get(0), equalTo("A"));
}
Copy the code

Now let’s see how we use a custom Comparator with sorted() :

@Test
public final void test() {   
    List<Human> humans = Lists.newArrayList(new Human("Sarah", 10), new Human("Jack", 12));
    Comparator<Human> nameComparator = (h1, h2) -> h1.getName().compareTo(h2.getName());
     
    List<Human> sortedHumans = humans.stream().sorted(nameComparator).collect(Collectors.toList());
    assertThat(sortedHumans.get(0), equalTo(new Human("Jack", 12)));
}
Copy the code

If we use the Comparator.comparing() method, we can further simplify the above example:

@Test
public final void test() {
    List<Human> humans = Lists.newArrayList(new Human("Sarah", 10), new Human("Jack", 12));
  
    List<Human> sortedHumans = humans.stream()
      .sorted(Comparator.comparing(Human::getName))
      .collect(Collectors.toList());
       
    assertThat(sortedHumans.get(0), equalTo(new Human("Jack", 12)));
}
Copy the code

Stream reverse sort

We can also use stream.sorted () to reverse sort the List.

First, let’s look at an example of how to combine the sorted() method with comparator.reverseorder () to sort a list in reverseOrder:

@Test
public final void test() {
    List<String> letters = Lists.newArrayList("B"."A"."C");
 
    List<String> reverseSortedLetters = letters.stream()
      .sorted(Comparator.reverseOrder())
      .collect(Collectors.toList());
       
    assertThat(reverseSortedLetters.get(0), equalTo("C"));
}
Copy the code

Now, let’s see how to use the sorted() method with a custom Comparator:


@Test
public final void test() {
    List<Human> humans = Lists.newArrayList(new Human("Sarah", 10), new Human("Jack", 12));
    Comparator<Human> reverseNameComparator = (h1, h2) -> h2.getName().compareTo(h1.getName());
 
    List<Human> reverseSortedHumans = humans.stream().sorted(reverseNameComparator)
      .collect(Collectors.toList());
    assertThat(reverseSortedHumans.get(0), equalTo(new Human("Sarah", 10)));
}

Copy the code

Finally, let’s simplify the example using the Comparator.comparing() method:

@Test
public final void test() {
    List<Human> humans = Lists.newArrayList(new Human("Sarah", 10), new Human("Jack", 12));
 
    List<Human> reverseSortedHumans = humans.stream()
      .sorted(Comparator.comparing(Human::getName, Comparator.reverseOrder()))
      .collect(Collectors.toList());
     
    assertThat(reverseSortedHumans.get(0), equalTo(new Human("Sarah", 10)));
}
Copy the code

conclusion

The use of Java 8 Lambda expressions to sort lists is very useful and is one of the scenarios where Lambda is used, demonstrating the semantic power of Lambda.