Ръководство за Java 8 Comparator.comparing ()

1. Общ преглед

Java 8 представи няколко подобрения в интерфейса за сравнение , включително шепа статични функции, които са от голяма полза при изготвянето на ред за сортиране на колекции.

Ламбда Java 8 може да се използва ефективно и с интерфейса за сравнение . Подробно обяснение на ламбдите и Comparator можете да намерите тук, а хроника за сортирането и приложенията на Comparator можете да намерите тук.

В този урок ще разгледаме няколко функции, въведени за интерфейса за сравнение в Java 8 .

2. Първи стъпки

2.1. Примерен клас боб

За примерите в тази статия, нека създадем боб на служител и използваме неговите полета за сравнение и сортиране:

public class Employee { String name; int age; double salary; long mobile; // constructors, getters & setters }

2.2. Нашите данни за тестване

Нека също така създадем набор от служители, които ще се използват за съхраняване на резултатите от нашия тип в различни тестови случаи в статията:

employees = new Employee[] { ... };

Първоначалното подреждане на елементи на служителите ще бъде:

[Employee(name=John, age=25, salary=3000.0, mobile=9922001), Employee(name=Ace, age=22, salary=2000.0, mobile=5924001), Employee(name=Keith, age=35, salary=4000.0, mobile=3924401)]

В цялата статия ще сортираме над масива на служителите, използвайки различни функции.

За тестови твърдения ще използваме набор от предварително сортирани масиви, които ще сравним с резултатите от сортирането (т.е. масива от служители ) за различни сценарии.

Нека декларираме няколко от тези масиви:

@Before public void initData() { sortedEmployeesByName = new Employee[] {...}; sortedEmployeesByNameDesc = new Employee[] {...}; sortedEmployeesByAge = new Employee[] {...}; // ... }

Както винаги, не се колебайте да се обърнете към нашата връзка към GitHub за пълния код.

3. Използване на Comparator.comparing

Този раздел обхваща варианти на сравнителната статична функция Comparator.com .

3.1. Вариант за избор на ключ

В Comparator.comparing статична функция приема един вид ключ Функция и връща компаратор за вида, който съдържа ключа за сортиране:

static 
    
      Comparator comparing( Function keyExtractor)
    

За да видим това в действие, нека използваме полето за име в Служител като ключ за сортиране и предаваме референцията му към метод като аргумент от тип Функция В сравнителен продукт се върна от същите се използва за сортиране:

@Test public void whenComparing_thenSortedByName() { Comparator employeeNameComparator = Comparator.comparing(Employee::getName); Arrays.sort(employees, employeeNameComparator); assertTrue(Arrays.equals(employees, sortedEmployeesByName)); }

Както можете да видите, стойностите на масива на служителите се сортират по име в резултат на сортирането:

[Employee(name=Ace, age=22, salary=2000.0, mobile=5924001), Employee(name=John, age=25, salary=3000.0, mobile=9922001), Employee(name=Keith, age=35, salary=4000.0, mobile=3924401)] 

3.2. Вариант за избор на ключ и сравнение

Има и друга опция, която улеснява отменянето на естественото подреждане на ключа за сортиране, като предоставя сравнителя, който създава персонализирано подреждане за ключа за сортиране:

static  Comparator comparing( Function keyExtractor, Comparator keyComparator)

Нека модифицираме теста по-горе, като заменим естествения ред на сортиране по полето за име, като предоставим Comparator за сортиране на имената в низходящ ред като втори аргумент на Comparator.comparing :

@Test public void whenComparingWithComparator_thenSortedByNameDesc() { Comparator employeeNameComparator = Comparator.comparing( Employee::getName, (s1, s2) -> { return s2.compareTo(s1); }); Arrays.sort(employees, employeeNameComparator); assertTrue(Arrays.equals(employees, sortedEmployeesByNameDesc)); }

Както можете да видите, резултатите са сортирани в низходящ ред по име :

[Employee(name=Keith, age=35, salary=4000.0, mobile=3924401), Employee(name=John, age=25, salary=3000.0, mobile=9922001), Employee(name=Ace, age=22, salary=2000.0, mobile=5924001)]

3.3. Използване на Comparator.reversed

При извикване на съществуващ Comparator , екземплярният метод Comparator.reversed връща нов Comparator, който обръща реда на сортиране на оригинала.

Нека използваме компаратора, който сортира служителите по име и да го обърне , така че служителите да бъдат сортирани в низходящ ред на името :

@Test public void whenReversed_thenSortedByNameDesc() { Comparator employeeNameComparator = Comparator.comparing(Employee::getName); Comparator employeeNameComparatorReversed = employeeNameComparator.reversed(); Arrays.sort(employees, employeeNameComparatorReversed); assertTrue(Arrays.equals(employees, sortedEmployeesByNameDesc)); }

Резултатите са сортирани в низходящ ред по име :

[Employee(name=Keith, age=35, salary=4000.0, mobile=3924401), Employee(name=John, age=25, salary=3000.0, mobile=9922001), Employee(name=Ace, age=22, salary=2000.0, mobile=5924001)]

3.4. Използване на Comparator.comparingInt

Има и функция Comparator.comparingInt, която прави същото като Comparator.comparing , но отнема само int селектори. Нека опитаме това с пример, при който поръчваме служители по възраст :

@Test public void whenComparingInt_thenSortedByAge() { Comparator employeeAgeComparator = Comparator.comparingInt(Employee::getAge); Arrays.sort(employees, employeeAgeComparator); assertTrue(Arrays.equals(employees, sortedEmployeesByAge)); }

Нека видим как се подреждат стойностите на масива на служителите след сортирането:

[Employee(name=Ace, age=22, salary=2000.0, mobile=5924001), Employee(name=John, age=25, salary=3000.0, mobile=9922001), Employee(name=Keith, age=35, salary=4000.0, mobile=3924401)]

3.5. Използване на Comparator.comparingLong

Подобно на това, което направихме за ключовете int , нека видим пример, използващ Comparator.comparingLong, за да разгледаме ключ за сортиране от тип long, като подредим масива на служителите от мобилното поле:

@Test public void whenComparingLong_thenSortedByMobile() { Comparator employeeMobileComparator = Comparator.comparingLong(Employee::getMobile); Arrays.sort(employees, employeeMobileComparator); assertTrue(Arrays.equals(employees, sortedEmployeesByMobile)); }

Let's see how the employees array values are ordered after the sort with mobile as the key:

[Employee(name=Keith, age=35, salary=4000.0, mobile=3924401), Employee(name=Ace, age=22, salary=2000.0, mobile=5924001), Employee(name=John, age=25, salary=3000.0, mobile=9922001)]

3.6. Using Comparator.comparingDouble

Again, similar to what we did for int and long keys, let's see an example using Comparator.comparingDouble to consider a sort key of type double by ordering the employees array by the salary field:

@Test public void whenComparingDouble_thenSortedBySalary() { Comparator employeeSalaryComparator = Comparator.comparingDouble(Employee::getSalary); Arrays.sort(employees, employeeSalaryComparator); assertTrue(Arrays.equals(employees, sortedEmployeesBySalary)); }

Let's see how the employees array values are ordered after the sort with salary as the sort key:

[Employee(name=Ace, age=22, salary=2000.0, mobile=5924001), Employee(name=John, age=25, salary=3000.0, mobile=9922001), Employee(name=Keith, age=35, salary=4000.0, mobile=3924401)]

4. Considering Natural Order in Comparator

The natural order is defined by the behavior of the Comparable interface implementation. More information about the difference between Comparator and uses of the Comparable interface can be found in this article.

Let's implement Comparable in our Employee class so that we can try the naturalOrder and reverseOrder functions of the Comparator interface:

public class Employee implements Comparable{ // ... @Override public int compareTo(Employee argEmployee) { return name.compareTo(argEmployee.getName()); } }

4.1. Using Natural Order

The naturalOrder function returns the Comparator for the return type mentioned in the signature:

static 
    
      Comparator naturalOrder()
    

Given the above logic to compare employees based on name field, let's use this function to obtain to a Comparator which sorts the employees array in natural order:

@Test public void whenNaturalOrder_thenSortedByName() { Comparator employeeNameComparator = Comparator. naturalOrder(); Arrays.sort(employees, employeeNameComparator); assertTrue(Arrays.equals(employees, sortedEmployeesByName)); }

Let's see how the employees array values are ordered after the sort:

[Employee(name=Ace, age=22, salary=2000.0, mobile=5924001), Employee(name=John, age=25, salary=3000.0, mobile=9922001), Employee(name=Keith, age=35, salary=4000.0, mobile=3924401)]

4.2. Using Reverse Natural Order

Similar to naturalOrder, let's use the reverseOrder method to generate a Comparator which will produce a reverse ordering of employees to the one in the naturalOrder example:

@Test public void whenReverseOrder_thenSortedByNameDesc() { Comparator employeeNameComparator = Comparator. reverseOrder(); Arrays.sort(employees, employeeNameComparator); assertTrue(Arrays.equals(employees, sortedEmployeesByNameDesc)); }

Let's see how the employees array values are ordered after the sort:

[Employee(name=Keith, age=35, salary=4000.0, mobile=3924401), Employee(name=John, age=25, salary=3000.0, mobile=9922001), Employee(name=Ace, age=22, salary=2000.0, mobile=5924001)]

5. Considering Null Values in Comparator

This section covers functions nullsFirst and nullsLast, which consider null values in ordering and keep the null values at the beginning or end of the ordering sequence.

5.1. Considering Null First

Let's randomly insert null values in employees array:

[Employee(name=John, age=25, salary=3000.0, mobile=9922001), null, Employee(name=Ace, age=22, salary=2000.0, mobile=5924001), null, Employee(name=Keith, age=35, salary=4000.0, mobile=3924401)]

The nullsFirst function will return a Comparator that keeps all nulls at the beginning of the ordering sequence:

@Test public void whenNullsFirst_thenSortedByNameWithNullsFirst() { Comparator employeeNameComparator = Comparator.comparing(Employee::getName); Comparator employeeNameComparator_nullFirst = Comparator.nullsFirst(employeeNameComparator); Arrays.sort(employeesArrayWithNulls, employeeNameComparator_nullFirst); assertTrue(Arrays.equals( employeesArrayWithNulls, sortedEmployeesArray_WithNullsFirst)); }

Let's see how the employees array values are ordered after the sort:

[null, null, Employee(name=Ace, age=22, salary=2000.0, mobile=5924001), Employee(name=John, age=25, salary=3000.0, mobile=9922001), Employee(name=Keith, age=35, salary=4000.0, mobile=3924401)]

5.2. Considering Null Last

The nullsLast function will return a Comparator that keeps all nulls at the end of the ordering sequence:

@Test public void whenNullsLast_thenSortedByNameWithNullsLast() { Comparator employeeNameComparator = Comparator.comparing(Employee::getName); Comparator employeeNameComparator_nullLast = Comparator.nullsLast(employeeNameComparator); Arrays.sort(employeesArrayWithNulls, employeeNameComparator_nullLast); assertTrue(Arrays.equals( employeesArrayWithNulls, sortedEmployeesArray_WithNullsLast)); }

Let's see how the employees array values are ordered after the sort:

[Employee(name=Ace, age=22, salary=2000.0, mobile=5924001), Employee(name=John, age=25, salary=3000.0, mobile=9922001), Employee(name=Keith, age=35, salary=4000.0, mobile=3924401), null, null]

6. Using Comparator.thenComparing

The thenComparing function lets you set up lexicographical ordering of values by provisioning multiple sort keys in a particular sequence.

Let's consider another array of Employee class:

someMoreEmployees = new Employee[] { ... };

Consider the following sequence of elements in the above array:

[Employee(name=Jake, age=25, salary=3000.0, mobile=9922001), Employee(name=Jake, age=22, salary=2000.0, mobile=5924001), Employee(name=Ace, age=22, salary=3000.0, mobile=6423001), Employee(name=Keith, age=35, salary=4000.0, mobile=3924401)]

Let's write a sequence of comparisons as age followed by the name and see the ordering of this array:

@Test public void whenThenComparing_thenSortedByAgeName(){ Comparator employee_Age_Name_Comparator = Comparator.comparing(Employee::getAge) .thenComparing(Employee::getName); Arrays.sort(someMoreEmployees, employee_Age_Name_Comparator); assertTrue(Arrays.equals(someMoreEmployees, sortedEmployeesByAgeName)); }

Here the ordering will be done by age, and for the values with the same age, ordering will be done by name. Let's observe this in the sequence we receive after sorting:

[Employee(name=Ace, age=22, salary=3000.0, mobile=6423001), Employee(name=Jake, age=22, salary=2000.0, mobile=5924001), Employee(name=Jake, age=25, salary=3000.0, mobile=9922001), Employee(name=Keith, age=35, salary=4000.0, mobile=3924401)]

Let's use the other version of thenComparing that is thenComparingInt, by changing the lexicographical sequence to name followed by age:

@Test public void whenThenComparing_thenSortedByNameAge() { Comparator employee_Name_Age_Comparator = Comparator.comparing(Employee::getName) .thenComparingInt(Employee::getAge); Arrays.sort(someMoreEmployees, employee_Name_Age_Comparator); assertTrue(Arrays.equals(someMoreEmployees, sortedEmployeesByNameAge)); }

Let's see how the employees array values are ordered after the sort:

[Employee(name=Ace, age=22, salary=3000.0, mobile=6423001), Employee(name=Jake, age=22, salary=2000.0, mobile=5924001), Employee(name=Jake, age=25, salary=3000.0, mobile=9922001), Employee(name=Keith, age=35, salary=4000.0, mobile=3924401)]

Similarly, there are functions thenComparingLong and thenComparingDouble for using long and double sorting keys.

7. Conclusion

Тази статия е ръководство за няколко функции, въведени в Java 8 за интерфейса за сравнение .

Както обикновено, изходният код може да бъде намерен в Github.