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?

Author: hkotsubo, 2020-05-18

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" than obj2 (i.e. if in an order obj1 must come before obj2), returns a negative number
  • if obj1 is "greater" than obj2 (i.e. if in an order obj1 should come after obj2), 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;
});
 3
Author: hkotsubo, 2020-05-19 13:49:12