“This is the ninth day of my participation in the First Challenge 2022. For details: First Challenge 2022”
Hello, I’m looking at the mountains.
This article is “Java advanced” column collected in the public number “see the mountain hut”, reply to “Java” available source code.
Sorting data is a common scenario in system development. Generally speaking, we can take two approaches:
- With the sorting function of the storage system (SQL, NoSQL, and NewSQL are all supported), the query result is the sorted result
- The query result is unordered data, sorted in memory.
Today we’re going to talk about the second sort, which is sort data in memory.
First, we define a base class from which we will later demonstrate sorting in memory.
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
private String name;
private int age;
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null|| getClass() ! = o.getClass()) {return false;
}
Student student = (Student) o;
return age == student.age && Objects.equals(name, student.name);
}
@Override
public int hashCode(a) {
returnObjects.hash(name, age); }}Copy the code
Based on theComparator
The sorting
Before Java8, we implemented the Comparator interface for sorting, for example:
new Comparator<Student>() {
@Override
public int compare(Student h1, Student h2) {
returnh1.getName().compareTo(h2.getName()); }};Copy the code
The definition of an anonymous inner class is shown here. If it is general contrast logic, you can define an implementation class directly. It is also relatively simple to use, as follows is the application:
@Test
void baseSortedOrigin(a) {
final List<Student> students = Lists.newArrayList(
new Student("Tom".10),
new Student("Jerry".12)); Collections.sort(students,new Comparator<Student>() {
@Override
public int compare(Student h1, Student h2) {
returnh1.getName().compareTo(h2.getName()); }}); Assertions.assertEquals(students.get(0), new Student("Jerry".12));
}
Copy the code
Junit5 is used here to implement the unit tests to verify that the logic fits well.
Because the defined Comparator uses the name field for sorting, in Java, String sorting is determined by single-character ASCII order, with J before T, so Jerry is first.
Lambda expression substitution is usedComparator
Anonymous inner class
Lamdba users of Java8 should know that anonymous inner classes can be simplified to Lambda expressions as follows:
Collections.sort(students, (Student h1, Student h2) -> h1.getName().compareTo(h2.getName()));
Copy the code
In Java8, the sort method was added to the List class, so collections.sort can be replaced directly with:
students.sort((Student h1, Student h2) -> h1.getName().compareTo(h2.getName()));
Copy the code
Following the type inference for Lambda in Java8, we can abbreviate the specified Student type:
students.sort((h1, h2) -> h1.getName().compareTo(h2.getName()));
Copy the code
So far, the whole sorting logic can be simplified as:
@Test
void baseSortedLambdaWithInferring(a) {
final List<Student> students = Lists.newArrayList(
new Student("Tom".10),
new Student("Jerry".12)); students.sort((h1, h2) -> h1.getName().compareTo(h2.getName())); Assertions.assertEquals(students.get(0), new Student("Jerry".12));
}
Copy the code
Extract common Lambda expressions by static methods
We can define a static method in Student:
public static int compareByNameThenAge(Student s1, Student s2) {
if (s1.name.equals(s2.name)) {
return Integer.compare(s1.age, s2.age);
} else {
returns1.name.compareTo(s2.name); }}Copy the code
This method needs to return a parameter of type int. In Java8, we can use this method in Lambda:
@Test
void sortedUsingStaticMethod(a) {
final List<Student> students = Lists.newArrayList(
new Student("Tom".10),
new Student("Jerry".12)); students.sort(Student::compareByNameThenAge); Assertions.assertEquals(students.get(0), new Student("Jerry".12));
}
Copy the code
With the help ofComparator
thecomparing
methods
In Java8, the Comparator class added a new comparing method that uses the passed Function argument as a comparison element, such as:
@Test
void sortedUsingComparator(a) {
final List<Student> students = Lists.newArrayList(
new Student("Tom".10),
new Student("Jerry".12)); students.sort(Comparator.comparing(Student::getName)); Assertions.assertEquals(students.get(0), new Student("Jerry".12));
}
Copy the code
Multiconditional sort
We showed multi-conditional sorting in the static methods section, and you can also implement multi-conditional logic in the Comparator anonymous inner class:
@Test
void sortedMultiCondition(a) {
final List<Student> students = Lists.newArrayList(
new Student("Tom".10),
new Student("Jerry".12),
new Student("Jerry".13)); students.sort((s1, s2) -> {if (s1.getName().equals(s2.getName())) {
return Integer.compare(s1.getAge(), s2.getAge());
} else {
returns1.getName().compareTo(s2.getName()); }}); Assertions.assertEquals(students.get(0), new Student("Jerry".12));
}
Copy the code
From a logical point of view, multi-condition sorting is to judge the first level of conditions, if they are equal, then judge the second level of conditions, and so on. In Java8, comparing and a series of thenComparing can be used to represent multi-level conditional judgments. The above logic can be simplified as:
@Test
void sortedMultiConditionUsingComparator(a) {
final List<Student> students = Lists.newArrayList(
new Student("Tom".10),
new Student("Jerry".12),
new Student("Jerry".13)); students.sort(Comparator.comparing(Student::getName).thenComparing(Student::getAge)); Assertions.assertEquals(students.get(0), new Student("Jerry".12));
}
Copy the code
The thenComparing method here can be multiple, which is used to represent multi-level conditional judgment, which is also the convenience of functional programming.
inStream
Sort by
In Java8, not only Lambda expressions were introduced, but also a new streaming API, the Stream API, which also has a sorted method for sorting elements in streaming computations. We can pass in a Comparator to implement sorting logic:
@Test
void streamSorted(a) {
final List<Student> students = Lists.newArrayList(
new Student("Tom".10),
new Student("Jerry".12));final Comparator<Student> comparator = (h1, h2) -> h1.getName().compareTo(h2.getName());
final List<Student> sortedStudents = students.stream()
.sorted(comparator)
.collect(Collectors.toList());
Assertions.assertEquals(sortedStudents.get(0), new Student("Jerry".12));
}
Copy the code
Similarly, we can use Lambda to simplify writing:
@Test
void streamSortedUsingComparator(a) {
final List<Student> students = Lists.newArrayList(
new Student("Tom".10),
new Student("Jerry".12));final Comparator<Student> comparator = Comparator.comparing(Student::getName);
final List<Student> sortedStudents = students.stream()
.sorted(comparator)
.collect(Collectors.toList());
Assertions.assertEquals(sortedStudents.get(0), new Student("Jerry".12));
}
Copy the code
Arranged in reverse chronological order
Reverse the sorting judgment
Sort by the values returned by the compareTo method. If you want to sort in reverse order, simply return the values returned:
@Test
void sortedReverseUsingComparator2(a) {
final List<Student> students = Lists.newArrayList(
new Student("Tom".10),
new Student("Jerry".12));final Comparator<Student> comparator = (h1, h2) -> h2.getName().compareTo(h1.getName());
students.sort(comparator);
Assertions.assertEquals(students.get(0), new Student("Tom".10));
}
Copy the code
H1.getname ().compareto (h2.getName())); h2.getName().compareto (h1.getName()) In the Java Collections defines a Java. Util. Collections. ReverseComparator internal private class, implementation elements reversal in this way.
Reversed order using the Comparator’s reversed method
The reversed method was added to Java8 and is also simple to use:
@Test
void sortedReverseUsingComparator(a) {
final List<Student> students = Lists.newArrayList(
new Student("Tom".10),
new Student("Jerry".12));final Comparator<Student> comparator = (h1, h2) -> h1.getName().compareTo(h2.getName());
students.sort(comparator.reversed());
Assertions.assertEquals(students.get(0), new Student("Tom".10));
}
Copy the code
inComparator.comparing
Defines sort inversion in
Comparing method and an overloaded methods, java.util.Com parator# comparing (Java. Util. The function. The function
, java.util.Comparator
), and the second parameter is passed to comparator.reverseorder (), which implements reverseOrder:
@Test
void sortedUsingComparatorReverse(a) {
final List<Student> students = Lists.newArrayList(
new Student("Tom".10),
new Student("Jerry".12)); students.sort(Comparator.comparing(Student::getName, Comparator.reverseOrder())); Assertions.assertEquals(students.get(0), new Student("Jerry".12));
}
Copy the code
inStream
Defines sort inversion in
The operation in Stream is similar to direct list sorting; you can reverse the Comparator definition or use comparator.reverseOrder (). The implementation is as follows:
@Test
void streamReverseSorted(a) {
final List<Student> students = Lists.newArrayList(
new Student("Tom".10),
new Student("Jerry".12));final Comparator<Student> comparator = (h1, h2) -> h2.getName().compareTo(h1.getName());
final List<Student> sortedStudents = students.stream()
.sorted(comparator)
.collect(Collectors.toList());
Assertions.assertEquals(sortedStudents.get(0), new Student("Tom".10));
}
@Test
void streamReverseSortedUsingComparator(a) {
final List<Student> students = Lists.newArrayList(
new Student("Tom".10),
new Student("Jerry".12));final List<Student> sortedStudents = students.stream()
.sorted(Comparator.comparing(Student::getName, Comparator.reverseOrder()))
.collect(Collectors.toList());
Assertions.assertEquals(sortedStudents.get(0), new Student("Tom".10));
}
Copy the code
Null value determination
In the previous examples, valued elements are sorted, which covers most scenarios, but sometimes we still encounter null elements:
- The element in the list is NULL
- The field in which the element in the list participates in the sorting condition is NULL
If we still use the previous implementation, we get NullPointException, or NPE, to briefly demonstrate:
@Test
void sortedNullGotNPE(a) {
final List<Student> students = Lists.newArrayList(
null.new Student("Snoopy".12),
null
);
Assertions.assertThrows(NullPointerException.class,
() -> students.sort(Comparator.comparing(Student::getName)));
}
Copy the code
So, we need to consider these scenarios.
The element is a clumsy implementation of NULL
The first thing that comes to mind is a short call:
@Test
void sortedNullNoNPE(a) {
final List<Student> students = Lists.newArrayList(
null.new Student("Snoopy".12),
null
);
students.sort((s1, s2) -> {
if (s1 == null) {
return s2 == null ? 0 : 1;
} else if (s2 == null) {
return -1;
}
return s1.getName().compareTo(s2.getName());
});
Assertions.assertNotNull(students.get(0));
Assertions.assertNull(students.get(1));
Assertions.assertNull(students.get(2));
}
Copy the code
We can extract the null logic into a Comparator, which can be achieved by combining:
class NullComparator<T> implements Comparator<T> {
private final Comparator<T> real;
NullComparator(Comparator<? super T> real) {
this.real = (Comparator<T>) real;
}
@Override
public int compare(T a, T b) {
if (a == null) {
return (b == null)?0 : 1;
} else if (b == null) {
return -1;
} else {
return (real == null)?0: real.compare(a, b); }}}Copy the code
This implementation was prepared for us in Java8.
useComparator.nullsLast
andComparator.nullsFirst
NullsLast implements null at the end:
@Test
void sortedNullLast(a) {
final List<Student> students = Lists.newArrayList(
null.new Student("Snoopy".12),
null
);
students.sort(Comparator.nullsLast(Comparator.comparing(Student::getName)));
Assertions.assertNotNull(students.get(0));
Assertions.assertNull(students.get(1));
Assertions.assertNull(students.get(2));
}
Copy the code
NullsFirst implements null at the beginning:
@Test
void sortedNullFirst(a) {
final List<Student> students = Lists.newArrayList(
null.new Student("Snoopy".12),
null
);
students.sort(Comparator.nullsFirst(Comparator.comparing(Student::getName)));
Assertions.assertNull(students.get(0));
Assertions.assertNull(students.get(1));
Assertions.assertNotNull(students.get(2));
}
Copy the code
Now let’s see how we can implement null logic for fields in a sort condition.
The field for the sort condition is NULL
NullsLast is implemented using two comparators:
@Test
void sortedNullFieldLast(a) {
final List<Student> students = Lists.newArrayList(
new Student(null.10),
new Student("Snoopy".12),
null
);
final Comparator<Student> nullsLast = Comparator.nullsLast(
Comparator.nullsLast( / / 1
Comparator.comparing(
Student::getName,
Comparator.nullsLast( / / 2
Comparator.naturalOrder() / / 3)))); students.sort(nullsLast); Assertions.assertEquals(students.get(0), new Student("Snoopy".12));
Assertions.assertEquals(students.get(1), new Student(null.10));
Assertions.assertNull(students.get(2));
}
Copy the code
The code logic is as follows:
- Code 1 is the first layer of null-safe logic that determines whether the element is null.
- Code 2 is the second layer of null-safe logic that determines whether the element’s conditional field is NULL.
- Code 3 is the condition
Comparator
This is used hereComparator.naturalOrder()
Because of the use ofString
Sorting, you could also write it as thetaString::compareTo
. If it’s a complex judgment, you can define a more complex oneComparator
The combination model is so good, one layer is not enough to set another layer.
At the end of the article to summarize
This article demonstrates the use of Lambda expressions in Java8 to implement a variety of sorting logic, the new syntax is really sweet.
Green hills never change, green waters always flow. See you next time.
Recommended reading
- This article describes 24 operations for Java8 Stream Collectors
- Java8 Optional 6 kinds of operations
- Use Lambda expressions to achieve super sorting functions
- Java8 Time Library (1) : Describes the time class and common apis in Java8
- Java8 time library (2) : Convert Date to LocalDate or LocalDateTime
- Java8 Time Library (3) : Start using Java8 time classes
- Java8 time library (4) : check if the date string is valid
- New features in Java8
- New features in Java9
Hello, I’m looking at the mountains. Swim in the code, play to enjoy life. If this article is helpful to you, please like, bookmark, follow.