brief introduction
In this tutorial, we will first learn about Lambda support in Java 8, especially how to use it to write Comparator s and sort Collection s.
First, let's define a simple entity class:
public class Human { private String name; private int age; }
Simple sorting of lists
Prior to Java 8, sorting collections would involve creating anonymous inner classes for Comparator used in sorting:
new Comparator<Human>() { @Override public int compare(Human h1, Human h2) { return h1.getName().compareTo(h2.getName()); } }
This is relatively simple. Let me look at the case of unit testing.
@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) { return h1.getName().compareTo(h2.getName()); } }); Assert.assertThat(humans.get(0), equalTo(new Human("Jack", 12))); }
The Use of Lambda in Sorting
With the introduction of Lambdas, we can now bypass anonymous inner classes and achieve the same results through simple, functional semantic implementations:
(final Human h1, final Human h2) -> h1.getName().compareTo(h2.getName());
Again, you can use previous test cases:
@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))); }
Note that we also used the new sort API added to java.util.List in Java 8, rather than the old Collections.sort API.
Sorting without Type Definition
We can further simplify expressions by not specifying type definitions - compilers can infer these by themselves:
(h1, h2) -> h1.getName().compareTo(h2.getName())
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))); }
Thanks to Lambda's method support, this makes my code more concise.
Sorting using static methods
Next, we will use Lambda Expression to perform sorting and refer to static methods.
First, we will define the method compare ByNameThenAge with the comparison method in the Comparator < Human > object with exactly the same return value:
public static int compareByNameThenAge(Human lhs, Human rhs) { if (lhs.name.equals(rhs.name)) { return lhs.age - rhs.age; } else { return lhs.name.compareTo(rhs.name); } }
Now let's see how to use it.
humans.sort(Human::compareByNameThenAge);
Look at unit testing
@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))); }
Sorting of internal API s
We can also compare sorting by using Collections references and Comparator. comparison method combinations.
We will use getName() to construct Lambda expressions and sort lists 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))); }
Reverse sorting
Java 8 also introduces an auxiliary method for inverting comparators, which we can quickly use to invert our sorting:
@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))); }
Multiple Conditional Sorting
Comparing lambda expressions is not necessarily very simple. We can also write more complex expressions. For example, sort and compare 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 { return lhs.getName().compareTo(rhs.getName()); } }); Assert.assertThat(humans.get(0), equalTo(new Human("Sarah", 10))); }
Multi-Conditional Combination Sorting
For the same example, we can also use Comparator's new combination support.
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))); }
Stream sorting
We can also sort collections using the Stream sorted() API of Java 8.
We can sort stream s using natural sort and sort provided by comparators. To this end, we have sorted(), which corresponds to two API s:
- Sort (); Sort Stream's elements by sorting, and the element class must implement the Comparable interface
- Sorted (Comparator <? Super T > comparator); sort elements according to Comparator instances
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")); }
Now let's see how we can use custom Comparator and 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))); }
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))); }
Stream reverse sort
We can also use Stream.sorted() to reverse sort lists.
First, let's look at an example of how to sort lists in reverse order by combining the sorted() method with Comparator.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")); }
Now let's see how to use the sorted() method and customize 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))); }
Finally, let's use the Comparator.comparing() method to simplify the above example:
@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))); }
summary
Using Java 8 Lambda expression to sort List s is very good, and it is also one of Lambda's usage scenarios, which shows the powerful semantic function of Lambda.