Sort using tiebreaker criteria
I need to sort a list of objects with the following criteria:
- higher average proficiency levels in each of the
technical skills required for the task; - if the averages are equal, tie at the lowest price(
valorPretendido
); - if prices are equal, tiebreaker for earlier registered bid (
date
);
public class Candidatura{
private Date date;
private double valorPretendido;
private float media;
private int nrDias;
private String txtApresentacao;
private String txtMotivacao;
int nrCandidaturas = 0;
public Candidatura (Date dataCandidatura, double valorPretendido, float media, int nrDias, String txtApresentacao,String txtMotivacao) {
this.setDate(dataCandidatura);
this.setValorPretendido(valorPretendido);
this.setMedia(media)
this.setNrDias(nrDias);
this.setTxtApresentacao(txtApresentacao);
this.setTxtMotivacao(txtMotivacao);
nrCandidaturas++;
}
I will have to do sort
several times until there is only one object that can remain after all the eliminations? Is it possible to order only once to get the" biggest " element?
1 answers
Yes, you can do everything in one sort
.
If you want to make a specific sort, you can use Collections.sort
passing a Comparator
as a parameter.
The idea of Comparator
is to provide a customized form of comparison. Just implement the method compare
, which receives two objects obj1
and obj2
and returns the following:
- if
obj1
is "less" thanobj2
(i.e. if in an orderobj1
must come beforeobj2
), returns a negative number - if
obj1
is "greater" thanobj2
(i.e. if in an orderobj1
should come afterobj2
), returns a positive number - if whatever the order between them, returns zero
Then given a list of objects, just implement a Comparator
that follows the rules you need:
List<Candidatura> list = new ArrayList<>();
// adiciona Candidaturas na lista
Collections.sort(list, new Comparator<Candidatura>() {
@Override
public int compare(Candidatura c1, Candidatura c2) {
int cmp = Double.compare(c2.getMedia(), c1.getMedia());
if (cmp == 0) { // se as médias forem iguais, desempata pelo menor preço
cmp = Double.compare(c1.getValorPretendido(), c2.getValorPretendido());
if (cmp == 0) { // se os preços forem iguais, desempata pela menor data
cmp = c1.getDate().compareTo(c2.getDate());
}
}
return cmp;
}
});
System.out.println(list.get(0));
First I compare the averages. If they are equal (and only if they are equal), I compare the price. If the price is equal, I compare the date.
Notice that for the average I compared c2
with c1
. If I reversed the order, they would be in ascending order, but when I reversed, they were in descending order. This means that the highest averages will be at the top of the list. Already for the price and date, I put c1
first, because the smallest values should stay before.
At the end, the element you want will be in the first position (the one with the highest average, and in case of a tie on the average, the one with the lowest price, and in case of a tie in price and average, which has the lowest date). Also remembering that if you have a tie in the first position (two elements with the same average, price and date), the one that appears first in the list will be returned (since the ordering is guaranteed to be stable ).
From Java 8 you can also use the syntax of lambda :
Collections.sort(list, (c1, c2) -> {
int cmp = Double.compare(c2.getMedia(), c1.getMedia());
if (cmp == 0) { // se as médias forem iguais, desempata pelo menor preço
cmp = Double.compare(c1.getValorPretendido(), c2.getValorPretendido());
if (cmp == 0) { // se os preços forem iguais, desempata pela menor data
cmp = c1.getDate().compareTo(c2.getDate());
}
}
return cmp;
});