Comparador, coleções de classificação - 1

"Olá, amigo!"

"Oi, Bilaabo!"

"Hoje examinaremos um tópico pequeno, mas interessante e útil: classificação de coleções."

"Classificar? Eu ouvi algo sobre isso."

"Há muito tempo, todo programador tinha que ser capaz de escrever algoritmos de classificação. Era capaz e tinha que escrevê-los. Mas esses dias acabaram. Hoje, escrever seu próprio código de classificação é considerado ruim, assim como reescrever qualquer outra coisa que já tenha foi inventado".

"Em Java (e outras linguagens de programação), a classificação já está implementada.  Sua tarefa é aprender a usar corretamente o que já existe. "

"OK."

"A classe auxiliar Collections tem um método de classificação estático que é usado para classificar coleções — ou mais precisamente, listas. Elementos em Maps e Sets não têm uma ordem/índice, então não há nada para classificar."

"Sim, eu me lembro. Usei esse método uma vez para classificar uma lista de números."

"Ótimo. Mas esse método é muito mais poderoso do que parece à primeira vista. Ele pode classificar não apenas números, mas também quaisquer objetos, com base em qualquer critério. Duas interfaces ajudam o método a fazer isso: Comparable e Comparator . "

"Às vezes, você precisa classificar objetos, não números. Por exemplo, suponha que você tenha uma lista de pessoas e queira classificá-las por idade. Temos a interface Comparable para isso."

"Deixe-me primeiro mostrar um exemplo e então tudo ficará mais claro:"

Exemplo
public class Woman implements Comparable<Woman>
{
public int age;

public Woman(int age) {
this.age = age;
}

public int compareTo(Woman o)
{
return this.age - o.age;
}
}
Um exemplo de como pode ser usado:
public static void main(String[] args )
{
ArrayList<Woman> women = new ArrayList<Woman>();
women.add(new Woman(18));
women.add(new Woman(21));
women.add(new Woman(5));

Collections.sort(women);
}

"Para classificar objetos, você deve primeiro saber compará-los. Para isso, usamos Comparable. A interface Comparable é genérica, o que significa que aceita um argumento de tipo. Ela possui apenas um método genérico: compareTo(T o). Este método compara o objeto atual (this) e um objeto passado como argumento (o), ou seja, precisamos implementar este método em nossa classe e então usá-lo para comparar o objeto atual (this) com o objeto passado. "

"E como o compareTo funciona? Eu esperava que ele retornasse true ou false dependendo se o objeto passado era maior ou menor."

"As coisas são mais complicadas aqui. O método compareTo não retorna verdadeiro/falso. Em vez disso, ele retorna um int. Na verdade, isso é feito para simplificar.

"Quando um computador precisa determinar se um número é maior que outro, ele simplesmente subtrai o segundo número do primeiro e então olha para o resultado. Se o resultado for 0, então os números são iguais. Se o resultado for menor que zero , então o segundo número é maior. E se o resultado for maior que zero, então o primeiro número é maior."

"A mesma lógica se aplica aqui. De acordo com a especificação, o método compareTo deve retornar zero se os objetos comparados forem iguais. Se o método compareTo retornar um número maior que zero, então nosso objeto é maior que o objeto passado. "Se o método compareTo retornar retorna um número menor que zero, então 'this' é menor que o objeto passado."

"Isso é um pouco estranho."

"Sim, mas se você estiver comparando objetos simplesmente com base em alguma propriedade numérica, poderá apenas retornar a diferença entre eles subtraindo um do outro. Exatamente como foi feito no exemplo acima."

public int compareTo(Woman o)
{
return this.age - o.age;
}

"Acho que entendo tudo. Mas talvez não. Mas quase tudo."

"Ótimo. Agora vamos considerar um problema mais prático. Suponha que você tenha escrito um site interessante para fazer roupas femininas na China. Você usa uma classe Woman para descrever seus clientes. Você até fez uma página da web com uma tabela onde você pode vê-los todos . Mas há um problema..."

"Seu objeto Woman contém não apenas uma idade, mas também um monte de outros dados: nome, sobrenome, altura, peso, número de filhos, etc."

"A tabela de usuários tem muitas colunas, e aqui está a pergunta: como você classifica seus usuários pelos vários critérios? Por peso, por idade, por sobrenome?"

"Hmm. Sim, muitas vezes vejo tabelas que permitem classificar por coluna. Então, como você faz isso?"

"Para isso, temos a segunda interface da qual queria falar hoje: a interface Comparator. Ela também tem um método compare, mas leva dois argumentos, não um: int compare(T o1, T o2). Veja como funciona funciona:"

Exemplo
public class Woman
{
public int age;
public int childrenCount;
public int weight;
public int height;
public String name;

public Woman(int age) {
this.age = age;
}
}
Um exemplo de como pode ser usado:
public static void main(String[] args )
{
ArrayList<Woman> women = new ArrayList<Woman>();
women.add(new Woman(18));
women.add(new Woman(21));
women.add(new Woman(5));

Comparator<Woman> compareByHeight = new Comparator<Woman>() {
public int compare(Woman o1, Woman o2) {
return o1.height - o2.height;
}
};

Collections.sort(women, compareByHeight);
}

"A interface Comparator não esconde a lógica de comparação de objetos dentro da classe dos objetos que estão sendo comparados. Em vez disso, ela é implementada em uma classe separada."

"Então, eu poderia criar várias classes que implementassem a interface Comparator e fazer com que cada uma delas comparasse diferentes propriedades? Peso em uma, idade em outra e altura em uma terceira?"

"Sim, é muito simples e conveniente."

"Apenas chamamos o método Collections.sort , passando uma lista de objetos e outro objeto especial como segundo argumento, que implementa a interface Comparator e informa como comparar pares de objetos corretamente no processo de classificação."

"Hmm. Acho que entendi tudo. Deixe-me tentar. Digamos que eu precise classificar os usuários por peso. Seria algo assim:"

Exemplo de classificação de usuários por peso:
Comparator<Woman> compareByWeight = new Comparator<Woman>() {
public int compare(Woman o1, Woman o2) {
return o1.weight - o2.weight;
}
};

Collections.sort(women, compareByWeight);

"Sim, exatamente."

"Ótimo. Mas e se eu quiser classificar na ordem inversa?"

"Pense nisso. A resposta é muito simples!"

"Eu entendi! Assim:"

Classificando em ordem crescente:
return o1.weight - o2.weight;
Classificando em ordem decrescente:
return o2.weight – o1.weight;

"Certo. Muito bem."

"E se eu quiser classificar pelo sobrenome? Como faço para classificar as strings, Bilaabo?"

"A classe String já implementa o método compareTo. Basta chamá-lo:"

Exemplo de classificação de usuários por nome:
Comparator<Woman> compareByName = new Comparator<Woman>() {
public int compare(Woman o1, Woman o2) {
return o1.name.compareTo(o2.name);
}
};

Collections.sort(women, compareByName);

"Foi uma grande lição, Bilaabo. Muito obrigado."

"E obrigado a você, meu amigo!"