Implement Comparator interface
The comparator interface can be found everywhere in the JDK library, from lookup to sorting, to reverse operation, and so on. Java 8 It becomes a functional interface, the advantage of which is that we can use the flow syntax to implement the comparator.
We use several different ways to realize comparator and see the value of new grammar. Your fingers will thank you for not realizing the anonymous inner class. How many keyboards do you have to knock?
Using Comparator to sort
The following example uses a different comparison method to sort a group of people. Let's start by creating a person's javabean.
Copy Code code as follows:
public class Person {
Private final String name;
private final int age;
Public person (final String thename, final int theage) {
name = Thename;
age = Theage;
}
Public String GetName () {return name;}
public int getage () {return age;}
public int agedifference (final person other) {
return age-other.age;
}
Public String toString () {
Return String.Format ('%s-%d ', name, age);
}
}
We can implement the comparator interface through the person class, but we can only use a comparison method. We want to compare different attributes--names, ages, or combinations of these. In order to be flexible to compare, we can use comparator, when we need to compare, then to generate the relevant code.
Let's start by creating a list of people, each with a different name and age.
Copy Code code as follows:
Final list<person> people = arrays.aslist (
New Person ("John", 20),
New Person ("Sara", 21),
New Person ("Jane", 21),
New Person ("Greg", 35));
We can sort them in ascending or descending order by person's name or age. The general approach is to use anonymous inner classes to implement the comparator interface. This writing is only relevant to the relative code is meaningful, the rest is just go formality. With lambda expressions, you can focus on the nature of the comparison.
We first sort them by age from small to large.
Now that we have a list object, we can sort it by using its sort () method. But this method also has its problem. This is a void method, which means that the list changes when we call this method. To keep the original list, we have to copy it first, and then call the sort () method. It's a bit of a struggle. This time we have to turn to the stream class.
We can get a stream object from the list and call its sorted () method. It returns a sorted collection instead of modifying it on the original set. Using this method, you can easily configure comparator parameters.
Copy Code code as follows:
List<person> Ascendingage =
People.stream ()
. Sorted ((Person1, Person2)-> person1.agedifference (Person2))
. Collect (ToList ());
Printpeople ("Sorted in Ascending": ", ascendingage);
We first convert the list into a stream object through the stream () method. It then calls its sorted () method. This method accepts a comparator parameter. Since comparator is a functional interface, we can pass in a lambda expression. Finally, we call the Collect method and let it store the results in a list. The Collect method is a reduction device that can output an object in an iterative process to a particular format or type. The ToList () method is a static method of the Collectors class.
The comparator abstract method CompareTo () receives two parameters, which is the object to compare, and returns the result of an int type. To be compatible with this, our lambda expression also receives two parameters, two person objects, and their types are automatically inferred by the compiler. We return an int type, indicating whether the objects of comparison are equal.
Because we want to sort by age, we compare the ages of two objects and then return the results of the comparison. If they are the same size, return 0. Otherwise, if the first person is younger, return a negative number, and the older word returns a positive number.
The sorted () method traverses each element of the target collection and invokes the specified comparator to determine the sort order of the elements. The sorted () method executes somewhat like the reduce () method mentioned earlier. The reduce () method progressively lists the list to a result. The sorted () method is sorted by the result of the comparison.
Once we have arranged the order, we want to print the results, so we call a Printpeople () method, and we implement the method below.
Copy Code code as follows:
public static void Printpeople (
Final String message, final list<person> people) {
SYSTEM.OUT.PRINTLN (message);
People.foreach (System.out::p rintln);
}
In this method, we first print a message and then iterate through the list, printing out each element inside.
Let's call the next sorted () method to see that it arranges the people in the list by age from small to large.
Copy Code code as follows:
Sorted in ascending:
John-20
Sara-21
Jane-21
Greg-35
Let's take a look at the sorted () method to make an improvement.
Copy Code code as follows:
. Sorted ((Person1, Person2)-> person1.agedifference (Person2))
In the lambda expression passed in, we simply routed the two arguments-the first argument as the invocation target of the Agedifference () method, and the second as its argument. But we can not write this, but in a office-space mode-that is, using the method reference, let the Java compiler to do the route.
The parameter routing used here is a bit different from what we saw earlier. As we saw before, either the argument is as the calling target or as the calling argument. And now, we have two parameters, we want to be divided into two parts, one as the target of the method invocation and the second as the argument. Don't worry, the Java compiler will tell you, "I'll take care of this."
We can replace the lambda expression in the previous sorted () method with a dapper Agedifference method.
Copy Code code as follows:
People.stream ()
. Sorted (Person::agedifference)
This code is very concise, thanks to the method reference provided by the Java compiler. The compiler receives the parameters of two person instances, using the first as the invocation target of the Agedifference () method, and the second as a method parameter. We let the compiler do the work, not write the code directly. When using this method, we have to make sure that the first parameter is the invocation target of the referenced method, and that the remainder is the method's entry.
Reusing comparator
It's easy to sort out the list of people by age from small to large, and of course it's easy to order from big to little. Let's try it on.
Copy Code code as follows:
Printpeople ("Sorted in Descending": ",
People.stream ()
. Sorted ((Person1, Person2)-> person2.agedifference (Person1))
. Collect (ToList ());
We call the sorted () method and pass in a lambda expression that fits into the comparator interface just as in the previous example. The only difference is the implementation of this lambda expression--we've tuned in the order of the people to compare. The result should be ranked by their age from large to small. Let's take a look.
Copy Code code as follows:
Sorted in descending:
Greg-35
Sara-21
Jane-21
John-20
Just changing the logic of comparison doesn't cost much. But we can't get this version to be referenced by the method because the order of the parameters does not conform to the rules of the parameter route referenced by the method; The first parameter is not used as the invocation target of the method, but as a method parameter. There is a way to solve this problem, and it can reduce duplication of work. Let's take a look at how to achieve it.
We have already created two lambda expressions: one is sorted by age from small to large, one is from large to smaller. In so doing, there will be code redundancy and repetition, and the dry principle is broken. If we just want to adjust the sort order, the JDK provides a reverse method that has a special method modifier, default. We'll discuss it in the default method on page 77, where we use this reversed () method to get rid of redundancy.
Copy Code code as follows:
Comparator<person> compareascending =
(Person1, Person2)-> person1.agedifference (Person2);
comparator<person> comparedescending = compareascending.reversed ();
We first created a comparator,compareascending to sort people by age from small to large. To reverse the comparison order, instead of writing the code again, we just need to call this first comparator reversed () method to get the second comparator object. At the bottom of the reversed () method, it creates a comparer to exchange the order of the parameters of the comparison. This means that reversed is also a higher-order method-it creates and returns a function that has no side effects. We put the two comparisons into the code.
Copy Code code as follows:
Printpeople ("Sorted in Ascending": ",
People.stream ()
. Sorted (compareascending)
. Collect (ToList ())
);
Printpeople ("Sorted in Descending": ",
People.stream ()
. Sorted (comparedescending)
. Collect (ToList ())
);
It is obvious from the code that these new features of Java8 greatly reduce the redundancy and complexity of the code, but the benefits are much more than that, and there is an infinite number of possibilities in the JDK for you to explore.
We've been able to sort by age, and it's easy to sort by name. We're going to order the dictionary by name, and again, just change the logic in the lambda expression.
Copy Code code as follows:
Printpeople ("Sorted in ascending order by name:",
People.stream ()
. Sorted ((Person1, Person2)->
Person1.getname (). CompareTo (Person2.getname ()))
. Collect (ToList ());
The results of the output are sorted by the dictionary order of the names.
Copy Code code as follows:
Sorted in ascending order by name:
Greg-35
Jane-21
John-20
Sara-21
So far, we have either sorted by age or sorted by name. We can make the logic of lambda expressions more intelligent. For example, we can sort by age and name at the same time.
Let's pick the youngest person on the list. We can sort by age from small to large and then select the first of the results. But there is no need to do that, Stream has a min () method to achieve this. This method also accepts a comparator, but returns the smallest object in the collection. Let's use it.
Copy Code code as follows:
People.stream ()
. Min (person::agedifference)
. ifpresent (Youngest-> System.out.println ("youngest:" + youngest));
We used the Agedifference method reference when calling the Min () method. The min () method returns a Optinal object because the list may be empty and there may be more than one youngest person in it. Then we get to the youngest person by Optinal's Ifprsend () method, and print out his details. Look at the output results.
Copy Code code as follows:
The oldest of the output is also very simple. Just pass this method reference to a Max () method.
Copy Code code as follows:
People.stream ()
. Max (Person::agedifference)
. ifpresent (eldest-> System.out.println ("eldest:" + eldest));
Let's take a look at the oldest name and age.
Copy Code code as follows:
With lambda expressions and method references, the implementation of the comparer becomes simpler and more convenient. The JDK has also introduced a number of handy ways to compararor classes so that we can compare them more smoothly, as we'll see below.
Multi-comparison and streaming comparisons
Let's take a look at the handy new methods provided by the comparator interface and use them to compare multiple properties.
We will continue to use the example in the previous section. Sorted by name, that's what we wrote above:
Copy Code code as follows:
People.stream ()
. Sorted ((Person1, Person2)->
Person1.getname (). CompareTo (Person2.getname ()));
This is simply too concise, compared with the internal classes of the last century. But if you use some of the functions in the comparator class to make it simpler, using these functions will allow us to express ourselves more smoothly. For example, to sort by name, we can write this:
Copy Code code as follows:
Final Function<person, string> byname = person-> person.getname ();
People.stream ()
. Sorted (Comparing (byname));
In this code we have imported the static method comparing () of the comparator class. The comparing () method uses the Passed-in lambda expression to generate a comparator object. That is, it is also a higher-order function that takes a function to the parameter and returns another function. In addition to making the grammar more concise, such code can be read to better describe the practical problems we want to solve.
With it, the multiple comparisons can also become smoother. For example, the following code, by name and age, can explain everything:
Copy Code code as follows:
Final Function<person, integer> byage = person-> person.getage ();
Final Function<person, string> bytheirname = person-> person.getname ();
Printpeople ("Sorted in Ascending" and name: ",
People.stream ()
. Sorted (Comparing (byage). thencomparing (Bytheirname))
. Collect (ToList ());
We first created two lambda expressions, one to return the age of the specified person, and one to return his name. When we call the sorted () method, we combine the two expressions together so that we can compare multiple properties. The comparing () method creates and returns a comparator by age, and we call the Thencomparing () method above the returned comparator to create a combined comparer that compares the age to the first name. The output below is the result of sorting by age and then by first name.
Copy Code code as follows:
Sorted in ascending and name:
John-20
Jane-21
Sara-21
Greg-35
As you can see, using lambda expressions and the new tool classes provided by JDK, it's easy to combine comparator implementations. Here we introduce the next collectors.