Comparar Objectos
Muitas vezes preciso de ir à base de dados e preencher listas (ou arrays) com objectos do tipo VO
(View Objects) para depois mostrar numa página web. Tenho necessidade de ordenar essa
lista baseado num critério qualquer. Supponhamos a seguinte situação: na base de dados
há uma tabela de pessoas com as colunas firstName, lastName, e age
e quero prencher uma lista com objectos do tipo Person:
public class Person {
private String firstName;
private String lastName;
private int age;
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public int getAge() {
return age;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public void setAge(int age) {
this.age = age;
}
}
Suponhamos agora que tenho um Array carregado com objectos do tipo Person
(com 3 objectos, por exemplo):
/**
* Hardcoded mas normalmente vem da base de dados sob a forma de resultset
*/
Person[] persons = new Person[3];
persons[0] = new Person();
persons[0].setFirstName("Marcus");
persons[0].setLastName("Miller");
persons[0].setAge(43);
persons[1] = new Person();
persons[1].setFirstName("Stanley");
persons[1].setLastName("Clark");
persons[1].setAge(35);
persons[2] = new Person();
persons[2].setFirstName("Jaco");
persons[2].setLastName("Pastorious");
persons[2].setAge(38);
Portanto, o que eu quero é ordenar este array por, digamos, primeiro nome ou idade.
Se utilizar o método sort da classe java.util.Arrays:
Arrays.sort(persons);
não funciona (envia uma excepção do tipo ClassCastException.
Naturalmente que posso escrever um algorítmo específico (tipo bubble sort) para ordenar
o array mas esta solução não é prática. A solução correcta passa por utilizar
o java.lang.Comparable interface. Implementando este interface,
torno as instâncias das classes comparáveis de acordo com critérios por mim definidos.
Vamos, então, implementar o interface Comparable. Este interface tem um
método, CompareTo, que determina como comparar duas instâncias
da classe. A sua assinatura é:
public int CompareTo(Object o)
que aceita como argumento um Object. Claro que só faz
sentido comparar instâncias do mesmo tipo. Podemos ver, também, que retorna um
inteiro que é igual a zero se o objecto passado é igual a esta instância e retorna
um inteiro positivo ou negativo se este objecto é maior ou menor do que o objecto
passado, respectivamente. Suponhamos que o critério de ordenação é a idade da pessoa:
A nossa classe Person fica assim:
public class Person implements Comparable{
private String firstName;
private String lastName;
private int age;
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public int getAge() {
return age;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public void setAge(int age) {
this.age = age;
}
public int compareTo(Object anotherPerson) throws ClassCastException{
if(!(anotherPerson instanceof Person))
throw new ClassCastException("Objecto do tipo Person esperado!");
int anotherPersonAge = ((Person)anotherPerson).getAge();
return this.age - anotherPersonAge;
}
}
Assim, se quiser ordenar o array por idades crescente, só tenho de fazer
Arrays.sort(Person).
Podemos testar:
public static void main(String[] args) {
Person[] persons = new Person[3];
persons[0] = new Person();
persons[0].setFirstName("Marcus");
persons[0].setLastName("Miller");
persons[0].setAge(43);
persons[1] = new Person();
persons[1].setFirstName("Stanley");
persons[1].setLastName("Clark");
persons[1].setAge(35);
persons[2] = new Person();
persons[2].setFirstName("Jaco");
persons[2].setLastName("Pastorious");
persons[2].setAge(38);
System.out.println("ORDEM NATURAL:");
for(int i = 0; i < persons.length; i++){
System.out.println(persons[i].getFirstName() + ", " +
persons[i].getLastName() + "," + persons[i].getAge());
}
Arrays.sort(persons);
System.out.println("ORDENADO POR IDADE");
for(int i = 0; i < persons.length; i++){
System.out.println(persons[i].getFirstName() + ", " +
persons[i].getLastName() + "," + persons[i].getAge());
}
}
e o output é o esperado:
ORDEM NATURAL: Marcus, Miller,43 Stanley, Clark,35 Jaco, Pastorious,38 ORDENADO POR IDADE Stanley, Clark,35 Jaco, Pastorious,38 Marcus, Miller,43
Se quiser ordenar, não por idade mas por, digamos, primeiro nome, só tenho de
adaptar o meu método CompareTo:
public int compareTo(Object anotherPerson) throws ClassCastException{
if(!(anotherPerson instanceof Person))
throw new ClassCastException("Objecto do tipo Person esperado!");
String anotherPersonFirstName = ((Person)anotherPerson).getFirstName();
return this.firstName.compareTo(anotherPersonFirstName);
}
E se eu quiser ter alguma flexibilidade no código para ordenar ou por idade, ou
por primeiro nome ou por último nome, conforme a situação? isso será matéria
para o próximo post (iremos implementar o interface java.util.Comparator