Classificação em Java

Classificação em Java

1. Visão geral

Este artigo ilustrará como aplicar a classificação aArray,List,SeteMap em Java 7 e Java 8.

2. Classificando comArray

Vamos começar classificando matrizes de inteiros primeiro usando o métodoArrays.sort().

Definiremos as seguintes matrizesint em um método jUnit@Before:

@Before
public void initVariables () {
    toSort = new int[]
      { 5, 1, 89, 255, 7, 88, 200, 123, 66 };
    sortedInts = new int[]
      {1, 5, 7, 66, 88, 89, 123, 200, 255};
    sortedRangeInts = new int[]
      {5, 1, 89, 7, 88, 200, 255, 123, 66};
    ...
}

2.1. Classificando Array Completo

Vamos agora usar a APIArray.sort() simples:

@Test
public void givenIntArray_whenUsingSort_thenSortedArray() {
    Arrays.sort(toSort);

    assertTrue(Arrays.equals(toSort, sortedInts));
}

A matriz não classificada agora está totalmente classificada:

[1, 5, 7, 66, 88, 89, 123, 200, 255]

As mentioned in the official JavaDoc,Arrays.sort usa Quicksort dual-pivot emprimitives. Ele oferece desempenho O (n log (n)) e normalmente é mais rápido que as implementações tradicionais do Quicksort (um pivô). No entanto, ele usa uma implementação iterativa, adaptativa e estável demergesort algorithm for Array of Objects.

2.2. Classificando parte de uma matriz

Arrays.sort tem mais uma APIsort - que discutiremos aqui:

Arrays.sort(int[] a, int fromIndex, int toIndex)

Isso classificará apenas uma parte da matriz, entre os dois índices.

Vamos dar uma olhada em um exemplo rápido:

@Test
public void givenIntArray_whenUsingRangeSort_thenRangeSortedArray() {
    Arrays.sort(toSort, 3, 7);

    assertTrue(Arrays.equals(toSort, sortedRangeInts));
}

A classificação será feita apenas nos seguintes elementos de submatriz (toIndex seria exclusivo):

[255, 7, 88, 200]

A sub-matriz classificada resultante, inclusive a matriz principal, seria:

[5, 1, 89, 7, 88, 200, 255, 123, 66]

2.3. Java 8Arrays.sort vsArrays.parallelSort

Java 8 vem com uma nova API -parallelSort - com uma assinatura semelhante à APIArrays.sort():

@Test
public void givenIntArray_whenUsingParallelSort_thenArraySorted() {
    Arrays.parallelSort(toSort);

    assertTrue(Arrays.equals(toSort, sortedInts));
}

Nos bastidores deparallelSort(),, ele divide a matriz em diferentes submatrizes (de acordo com a granularidade no algoritmo deparallelSort). Cada submatriz é classificada comArrays.sort() em diferentes threads de forma quesort possa ser executado em paralelo e são finalmente mesclados como uma matriz classificada.

Observe queForJoin common pool é usado para executar essas tarefas paralelas e, em seguida, mesclar os resultados.

O resultado deArrays.parallelSort vai ser o mesmo queArray.sort, claro, é apenas uma questão de aproveitar o multi-threading.

Finalmente, existem variantes semelhantes de APIArrays.sort emArrays.parallelSort também:

Arrays.parallelSort (int [] a, int fromIndex, int toIndex);

3. Classificando umList

Agora vamos usar a APICollections.sort() emjava.utils.Collections - para classificarList de inteiros:

@Test
public void givenList_whenUsingSort_thenSortedList() {
    List toSortList = Ints.asList(toSort);
    Collections.sort(toSortList);

    assertTrue(Arrays.equals(toSortList.toArray(),
    ArrayUtils.toObject(sortedInts)));
}

OList antes da classificação conterá os seguintes elementos:

[5, 1, 89, 255, 7, 88, 200, 123, 66]

E, naturalmente, após a classificação:

[1, 5, 7, 66, 88, 89, 123, 200, 255]

As mentioned in Oracle JavaDoc paraCollections.Sort, ele usa um mergesort modificado e oferece desempenhon log(n) garantido.

4. Classificando umSet

A seguir, vamos usarCollections.sort() para classificar umLinkedHashSet.

Estamos usandoLinkedHashSet porque ele mantém o pedido de inserção.

Observe como, para usar a APIsort emCollections -we’re first wrapping the set in a list:

@Test
public void givenSet_whenUsingSort_thenSortedSet() {
    Set integersSet = new LinkedHashSet<>(Ints.asList(toSort));
    Set descSortedIntegersSet = new LinkedHashSet<>(
      Arrays.asList(new Integer[]
        {255, 200, 123, 89, 88, 66, 7, 5, 1}));

    List list = new ArrayList(integersSet);
    Collections.sort(list, (i1, i2) -> {
        return i2 - i1;
    });
    integersSet = new LinkedHashSet<>(list);

    assertTrue(Arrays.equals(
      integersSet.toArray(), descSortedIntegersSet.toArray()));
}

5. ClassificandoMap

Nesta seção, começaremos olhandosorting a Map – both by keys and by values.

Vamos primeiro definir o mapa que iremos classificar:

@Before
public void initVariables () {
    ....
    HashMap map = new HashMap<>();
    map.put(55, "John");
    map.put(22, "Apple");
    map.put(66, "Earl");
    map.put(77, "Pearl");
    map.put(12, "George");
    map.put(6, "Rocky");
    ....
}

5.1. ClassificandoMap por chaves

Vamos agora extrair entradas dekeys evalues deHashMape classificá-las com base nos valores das chaves neste exemplo:

@Test
public void givenMap_whenSortingByKeys_thenSortedMap() {
    Integer[] sortedKeys = new Integer[] { 6, 12, 22, 55, 66, 77 };

    List> entries
      = new ArrayList<>(map.entrySet());
    Collections.sort(entries, new Comparator>() {
        @Override
        public int compare(
          Entry o1, Entry o2) {
            return o1.getKey().compareTo(o2.getKey());
        }
    });
    Map sortedMap = new LinkedHashMap<>();
    for (Map.Entry entry : entries) {
        sortedMap.put(entry.getKey(), entry.getValue());
    }

    assertTrue(Arrays.equals(sortedMap.keySet().toArray(), sortedKeys));
}

Observe como usamosLinkedHashMap ao copiar osEntries classificados com base nas chaves (porqueHashSet não garante a ordem das chaves).

OMap antes da classificação:

[Key: 66 , Value: Earl]
[Key: 22 , Value: Apple]
[Key: 6 , Value: Rocky]
[Key: 55 , Value: John]
[Key: 12 , Value: George]
[Key: 77 , Value: Pearl]

OMap após a classificaçãoby keys:

[Key: 6 , Value: Rocky]
[Key: 12 , Value: George]
[Key: 22 , Value: Apple]
[Key: 55 , Value: John]
[Key: 66 , Value: Earl]
[Key: 77 , Value: Pearl]

5.2. ClassificandoMap por valores

Aqui estaremos comparando os valores das entradas deHashMap para classificação com base nos valores deHashMap:

@Test
public void givenMap_whenSortingByValues_thenSortedMap() {
    String[] sortedValues = new String[]
      { "Apple", "Earl", "George", "John", "Pearl", "Rocky" };

    List> entries
      = new ArrayList<>(map.entrySet());
    Collections.sort(entries, new Comparator>() {
        @Override
        public int compare(
          Entry o1, Entry o2) {
            return o1.getValue().compareTo(o2.getValue());
        }
    });
    Map sortedMap = new LinkedHashMap<>();
    for (Map.Entry entry : entries) {
        sortedMap.put(entry.getKey(), entry.getValue());
    }

    assertTrue(Arrays.equals(sortedMap.values().toArray(), sortedValues));
}

OMap antes da classificação:

[Key: 66 , Value: Earl]
[Key: 22 , Value: Apple]
[Key: 6 , Value: Rocky]
[Key: 55 , Value: John]
[Key: 12 , Value: George]
[Key: 77 , Value: Pearl]

OMap após a classificaçãoby values:

[Key: 22 , Value: Apple]
[Key: 66 , Value: Earl]
[Key: 12 , Value: George]
[Key: 55 , Value: John]
[Key: 77 , Value: Pearl]
[Key: 6 , Value: Rocky]

6. Classificando objetos personalizados

Agora vamos trabalhar com um objeto personalizado:

public class Employee implements Comparable {
    private String name;
    private int age;
    private double salary;

    public Employee(String name, int age, double salary) {
        ...
    }

    // standard getters, setters and toString
}

Estaremos usando a seguinte matrizEmployee para exemplo de classificação nas seguintes seções:

@Before
public void initVariables () {
    ....
    employees = new Employee[] {
      new Employee("John", 23, 5000), new Employee("Steve", 26, 6000),
      new Employee("Frank", 33, 7000), new Employee("Earl", 43, 10000),
      new Employee("Jessica", 23, 4000), new Employee("Pearl", 33, 6000)};

    employeesSorted = new Employee[] {
      new Employee("Earl", 43, 10000), new Employee("Frank", 33, 70000),
      new Employee("Jessica", 23, 4000), new Employee("John", 23, 5000),
      new Employee("Pearl", 33, 4000), new Employee("Steve", 26, 6000)};

    employeesSortedByAge = new Employee[] {
      new Employee("John", 23, 5000), new Employee("Jessica", 23, 4000),
      new Employee("Steve", 26, 6000), new Employee("Frank", 33, 70000),
      new Employee("Pearl", 33, 4000), new Employee("Earl", 43, 10000)};
}

Podemos classificar matrizes ou coleções de objetos personalizados:

  1. na ordem natural (usando a interfaceComparable) ou

  2. na ordem fornecida por umComparatorInterface

6.1. Using Comparable

The natural order in java significa uma ordem na qual o primitivo ou Objeto deve ser ordenado ordenadamente em determinado array ou coleção.

Ambosjava.util.Arrays ejava.util.Collections têm um métodosort(), eIt’s highly recommended that natural orders should be consistent with the semantics of equals.

Neste exemplo, consideraremos os funcionários com o mesmoname como iguais:

@Test
public void givenEmpArray_SortEmpArray_thenSortedArrayinNaturalOrder() {
    Arrays.sort(employees);

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

Você pode definir a ordem natural dos elementos implementando uma interfaceComparable que tem o métodocompareTo() para comparar o objeto atual e o objeto passado como um argumento.

Para entender isso claramente, vamos ver um exemplo de classeEmployee que implementa a interfaceComparable:

public class Employee implements Comparable {
    ...

    @Override
    public boolean equals(Object obj) {
        return ((Employee) obj).getName().equals(getName());
    }

    @Override
    public int compareTo(Object o) {
        Employee e = (Employee) o;
        return getName().compareTo(e.getName());
    }
}

Geralmente, a lógica de comparação será escrita no métodocompareTo. Aqui, estamos comparando a ordem do funcionário ouname do campo do funcionário. Dois funcionários serão iguais se tiverem o mesmo nome.

Agora, quandoArrays.sort(employees); é chamado no código acima, agora sabemos qual é a lógica e a ordem que existe na classificação dos funcionários de acordo com a idade:

[("Earl", 43, 10000),("Frank", 33, 70000), ("Jessica", 23, 4000),
 ("John", 23, 5000),("Pearl", 33, 4000), ("Steve", 26, 6000)]

Podemos ver que a matriz é classificada pelo nome do funcionário - que agora se torna uma ordem natural para a classeEmployee.

6.2. UsandoComparator

Agora, vamos classificar os elementos usando uma implementação de interfaceComparator - onde passamos a classe interna anônima instantaneamente para a APIArrays.sort():

@Test
public void givenIntegerArray_whenUsingSort_thenSortedArray() {
    Integer [] integers = ArrayUtils.toObject(toSort);
    Arrays.sort(integers, new Comparator() {
        @Override
        public int compare(Integer a, Integer b) {
            return a - b;
        }
    });

    assertTrue(Arrays.equals(integers, ArrayUtils.toObject(sortedInts)));
}

Agora vamos classificar os funcionários com base emsalary - e passar outra implementação de comparador:

Arrays.sort(employees, new Comparator() {
    @Override
    public int compare(Employee o1, Employee o2) {
       return (int) (o1.getSalary() - o2.getSalary());
    }
 });

As matrizes de funcionários classificados com base emsalary serão:

[(Jessica,23,4000.0), (John,23,5000.0), (Pearl,33,6000.0), (Steve,26,6000.0),
(Frank,33,7000.0), (Earl,43,10000.0)]

Observe que podemos usarCollections.sort() de maneira semelhante para classificarListeSet de Objetos na ordem Natural ou Personalizada, conforme descrito acima para Arrays.

7. Classificando com Lambdas

Comece com Java 8, podemos usar Lambdas para implementar a Interface FuncionalComparator.

Você pode dar uma olhada na redação deLambdas in Java 8 para aprimorar a sintaxe.

Vamos substituir o comparador antigo:

Comparator c  = new Comparator<>() {
    @Override
    public int compare(Integer a, Integer b) {
        return a - b;
    }
}

Com a implementação equivalente, usando a expressão Lambda:

Comparator c = (a, b) -> a - b;

Finalmente, vamos escrever o teste:

@Test
public void givenArray_whenUsingSortWithLambdas_thenSortedArray() {
    Integer [] integersToSort = ArrayUtils.toObject(toSort);
    Arrays.sort(integersToSort, (a, b) -> {
        return a - b;
    });

    assertTrue(Arrays.equals(integersToSort,
      ArrayUtils.toObject(sortedInts)));
}

Como você pode ver, uma lógica muito mais limpa e concisa aqui.

8. UsandoComparator.comparing eComparator.thenComparing

Java 8 vem com duas novas APIs úteis para classificação -comparing()ethenComparing() na interfaceComparator.

Eles são bastante úteis para encadear várias condições deComparator.

Vamos considerar um cenário onde podemos querer compararEmployee poragee então porname:

@Test
public void givenArrayObjects_whenUsingComparing_thenSortedArrayObjects() {
    List employeesList = Arrays.asList(employees);
    employees.sort(Comparator.comparing(Employee::getAge));

    assertTrue(Arrays.toString(employees.toArray())
      .equals(sortedArrayString));
}

Neste exemplo,Employee::getAge é a chave de classificação para a interfaceComparator que implementa uma interface funcional com função de comparação.

Esta é a variedade de funcionários após a classificação:

[(John,23,5000.0), (Jessica,23,4000.0), (Steve,26,6000.0), (Frank,33,7000.0),
(Pearl,33,6000.0), (Earl,43,10000.0)]

Aqui, os funcionários são classificados com base emage.

Podemos ver queJohn eJessica têm a mesma idade - o que significa que a lógica de ordem agora deve levar seus nomes em consideração - o que podemos conseguir comthenComparing():

...
employees.sort(Comparator.comparing(Employee::getAge)
  .thenComparing(Employee::getName));
...

Após a classificação com o trecho de código acima, os elementos na matriz de funcionários seriam classificados como:

[(Jessica,23,4000.0),
 (John,23,5000.0),
 (Steve,26,6000.0),
 (Frank,33,7000.0),
 (Pearl,33,6000.0),
 (Earl,43,10000.0)
]

Portanto,comparing()ethenComparing() definitivamente tornam os cenários de classificação mais complexos muito mais limpos para implementar.

Leitura adicional:

Guia de Classificação em Kotlin

Um breve guia para classificação usando a biblioteca padrão Kotlin.

Read more

9. Conclusão

Neste artigo, vimos como podemos aplicar a classificação aArray,List,Set eMap.

Também vimos uma breve introdução sobre como os recursos do Java 8 podem ser úteis na classificação, como o uso de Lambdas,comparing()ethenComparing()eparallelSort().

Todos os exemplos usados ​​no artigo estão disponíveisover on GitHub.