Guia do Java 8 Comparator.comparing ()

Guia do Java 8 Comparator.comparing ()

1. Visão geral

Java 8 introduziu vários aprimoramentos na interface deComparator, incluindo um punhado de funções estáticas que são de grande utilidade ao criar uma ordem de classificação para coleções.

Os lambdas do Java 8 também podem ser aproveitados efetivamente com a interfaceComparator. Uma explicação detalhada de lambdas eComparator pode ser encontradahere, e uma crônica sobre classificação e aplicações deComparator pode ser encontradahere.

Neste tutorial,we will explore several functions introduced for the Comparator interface in Java 8.

2. Começando

2.1. Amostra de classe de feijão

Para os exemplos neste artigo, vamos criar um beanEmployee e usar seus campos para fins de comparação e classificação:

public class Employee {
    String name;
    int age;
    double salary;
    long mobile;

    // constructors, getters & setters
}

2.2. Nossos dados de teste

Vamos também criar um conjunto de funcionários que serão usados ​​para armazenar os resultados do nosso tipo em vários casos de teste ao longo do artigo:

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

A ordem inicial dos elementos deemployees será:

[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)]

Ao longo do artigo, classificaremos acima da matrizEmployee usando diferentes funções.

Para asserções de teste, usaremos um conjunto de matrizes pré-classificadas que compararemos com nossos resultados de classificação (ou seja, a matrizemployees) para diferentes cenários.

Vamos declarar algumas dessas matrizes:

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

    // ...
}

Como sempre, sinta-se à vontade para consultar nossoGitHub link para obter o código completo.

3. UsandoComparator.comparing

Esta seção cobre as variantes da função estáticaComparator.comparing.

3.1. Variante do seletor de chave

A função estáticaComparator.comparing aceita uma chave de classificaçãoFunctione retornaComparator para o tipo que contém a chave de classificação:

static > Comparator comparing(
   Function keyExtractor)

Para ver isso em ação, vamos usar o camponame emEmployee como a chave de classificação e passar sua referência de método como um argumento do tipoFunction. OComparator retornado do mesmo é usado para classificar:

@Test
public void whenComparing_thenSortedByName() {
    Comparator employeeNameComparator
      = Comparator.comparing(Employee::getName);

    Arrays.sort(employees, employeeNameComparator);

    assertTrue(Arrays.equals(employees, sortedEmployeesByName));
}

Como você pode ver, os valores da matrizemployees são classificados por nome como resultado da classificação:

[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. Seletor de chave e varianteComparator

Há outra opção que facilita a substituição da ordem natural da chave de classificação, fornecendoComparator que cria uma ordem personalizada para a chave de classificação:

static  Comparator comparing(
  Function keyExtractor,
    Comparator keyComparator)

Vamos modificar o teste acima, substituindo a ordem natural de classificação pelo camponame, fornecendo umComparator para classificar os nomes em ordem decrescente como o segundo argumento paraComparator.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));
}

Como você pode ver, os resultados são classificados em ordem decrescente porname:

[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. UsandoComparator.reversed

Quando chamado em umComparator existente, o método de instânciaComparator.reversed retorna um novoComparator que inverte a ordem de classificação do original.

Vamos usar oComparator que classifica os funcionários pornameereverse para que os funcionários sejam classificados em ordem decrescente dename:

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

Os resultados são classificados em ordem decrescente porname:

[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. UsandoComparator.comparingInt

Também existe uma funçãoComparator.comparingInt que faz a mesma coisa queComparator.comparing, mas usa apenas os seletoresint. Vamos tentar isso com um exemplo onde ordenamosemployees porage:

@Test
public void whenComparingInt_thenSortedByAge() {
    Comparator employeeAgeComparator
      = Comparator.comparingInt(Employee::getAge);

    Arrays.sort(employees, employeeAgeComparator);

    assertTrue(Arrays.equals(employees, sortedEmployeesByAge));
}

Vamos ver como os valores da matrizemployees são ordenados após a classificação:

[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. UsandoComparator.comparingLong

Semelhante ao que fizemos para as chavesint, vamos ver um exemplo usandoComparator.comparingLong para considerar uma chave de classificação do tipolong ordenando a matrizemployees pormobilecampo s:

@Test
public void whenComparingLong_thenSortedByMobile() {
    Comparator employeeMobileComparator
      = Comparator.comparingLong(Employee::getMobile);

    Arrays.sort(employees, employeeMobileComparator);

    assertTrue(Arrays.equals(employees, sortedEmployeesByMobile));
}

Vamos ver como os valores da matrizemployees são ordenados após a classificação commobile como a chave:

[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. UsandoComparator.comparingDouble

Novamente, semelhante ao que fizemos para as chavesintelong, vamos ver um exemplo usandoComparator.comparingDouble para considerar uma chave de classificação do tipodouble ordenando oemployeesmatriz s pelo camposalary:

@Test
public void whenComparingDouble_thenSortedBySalary() {
    Comparator employeeSalaryComparator
      = Comparator.comparingDouble(Employee::getSalary);

    Arrays.sort(employees, employeeSalaryComparator);

    assertTrue(Arrays.equals(employees, sortedEmployeesBySalary));
}

Vamos ver como os valores da matrizemployees são ordenados após a classificação comsalary como a chave de classificação:

[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. Considerando a ordem natural emComparator

A ordem natural é definida pelo comportamento da implementação da interfaceComparable. Mais informações sobre a diferença entreComparatore usos da interfaceComparable podem ser encontradasin this article.

Vamos implementarComparable em nossa classeEmployee para que possamos tentar as funçõesnaturalOrderereverseOrder da interfaceComparator:

public class Employee implements Comparable{
    // ...

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

4.1. Usando ordem natural

A funçãonaturalOrder retornaComparator para o tipo de retorno mencionado na assinatura:

static > Comparator naturalOrder()

Dada a lógica acima para comparar funcionários com base no camponame, vamos usar esta função para obter umComparator que classifica a matrizemployees em ordem natural:

@Test
public void whenNaturalOrder_thenSortedByName() {
    Comparator employeeNameComparator
      = Comparator. naturalOrder();

    Arrays.sort(employees, employeeNameComparator);

    assertTrue(Arrays.equals(employees, sortedEmployeesByName));
}

Vamos ver como os valores da matrizemployees são ordenados após a classificação:

[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. Usando ordem natural reversa

Semelhante anaturalOrder, vamos usar o métodoreverseOrder para gerar umComparator que produzirá uma ordem reversa deemployees para aquele no exemplonaturalOrder:

@Test
public void whenReverseOrder_thenSortedByNameDesc() {
    Comparator employeeNameComparator
      = Comparator. reverseOrder();

    Arrays.sort(employees, employeeNameComparator);

    assertTrue(Arrays.equals(employees, sortedEmployeesByNameDesc));
}

Vamos ver como os valores da matrizemployees são ordenados após a classificação:

[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. Considerando valores nulos no comparador

Esta seção cobre as funçõesnullsFirstenullsLast, que consideram os valores denull no pedido e mantém os valoresnull no início ou no final da sequência de pedido.

5.1. Considerando Nulo Primeiro

Vamos inserir aleatoriamente os valores denull na matrizemployees:

[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)]

A funçãonullsFirst retornará umComparator que mantém todos osnulls no início da sequência de ordenação:

@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));
}

Vamos ver como os valores da matrizemployees são ordenados após a classificação:

[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. Considerando o último nulo

A funçãonullsLast retornará umComparator que mantém todos osnulls no final da sequência de pedido:

@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));
}

Vamos ver como os valores da matrizemployees são ordenados após a classificação:

[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. UsandoComparator.thenComparing

A funçãothenComparing permite que você configure a ordem lexicográfica de valores provisionando várias chaves de classificação em uma sequência específica.

Vamos considerar outro array de classeEmployee:

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

Considere a seguinte sequência de elementos na matriz acima:

[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)]

Vamos escrever uma sequência de comparações comoage seguida pornamee ver a ordem desta matriz:

@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));
}

Aqui a ordenação será feita porage, e para os valores com o mesmoage, a ordenação será feita porname. Vamos observar isso na sequência que recebemos após a classificação:

[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)]

Vamos usar a outra versão dethenComparing que éthenComparingInt, alterando a sequência lexicográfica paraname seguida porage:

@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));
}

Vamos ver como os valores da matrizemployees são ordenados após a classificação:

[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)]

Da mesma forma, existem funçõesthenComparingLongethenComparingDouble para usar as chaves de classificaçãolongedouble.

7. Conclusão

Este artigo é um guia para vários recursos introduzidos no Java 8 para a interfaceComparator.

Como de costume, o código-fonte pode ser encontradoover on Github.