Class that implements Comparable must compare by a String field in lexicographic order

Asked

Viewed 80 times

1

How do I order a list of people by name? For understanding, follow the code:

public class Pessoa implements Comparable<Pessoa> {
    
    private String nome;
    private int idade;
    private String sexo;
    
    Pessoa(String nome, int idade, String sexo) {
        this.nome = nome;
        this.idade = idade;
        this.sexo = sexo;
    }   

    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }
    
    public int getIdade() {
        return idade;
    }

    public void setIdade(int idade) {
        this.idade = idade;
    }

    @Override
    public int compareTo(Pessoa outra) {
        if(this.nome.length() < outra.getNome().length())
            return -1;
        if(this.nome.length() > outra.getNome().length())
            return 1;
        
        return 0;
    }

}

In the test class I call the method sort(), however, is not coming out lexicographically, because it is names (strings) was what I expected.

I know the class String implementing Comparable and in this case I’m dealing with an object Pessoa, but if I tell the class to implement Comparable, shouldn’t I be ordered lexicographically? How would I look in these cases? Where am I going wrong? You would have to load only strings in the list and not the whole object?

Test class:

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class TestaPessoa {
    
    public static void main(String[] args) {
        
        Pessoa p2 = new Pessoa("Joel", 34, "Masculino");
        Pessoa p6 = new Pessoa("Sebastião", 70, "Masculino");
        Pessoa p4 = new Pessoa("Ozeias", 41, "Masculino");
        Pessoa p5 = new Pessoa("Neuza", 59, "Feminino");
        Pessoa p1 = new Pessoa("Joabe", 33, "Masculino");
        Pessoa p3 = new Pessoa("Regiane", 37, "Feminino");
        
        
        List<Pessoa> pessoa = Arrays.asList(p2, p6, p4, p5, p1, p3);
        
        Collections.sort(pessoa);
        
        for(Pessoa p : pessoa) {
            System.out.println(p.getNome());
        }
        
    }
}

Exit:

Joel
Neuza
Joabe
Ozeias
Regiane
Sebastião

1 answer

2


length() returns the size of the string, so you are sorting by the number of characters in the name.

If you want the lexicographical order, compare the strings themselves. In this case, strings already have the method compareTo implemented, so just use it:

public int compareTo(Pessoa outra) {
    return this.nome.compareTo(outra.nome);
}

If you want to ignore the difference between upper and lower case letters, you can use compareToIgnoreCase.


Remembering that, case the class Pessoa did not implement Comparable, would also be possible to sort by name, using a Comparator:

Collections.sort(pessoa, new Comparator<Pessoa>() {
    @Override
    public int compare(Pessoa p1, Pessoa p2) {
        return p1.getNome().compareTo(p2.getNome());
    }
});

Or, from Java 8, just use method References:

Collections.sort(pessoa, Comparator.comparing(Pessoa::getNome));
  • 2

    And to ensure that nome will not be null, I suggest in the builder this.nome = Objects.requireNonNull(nome);

  • Instead of making an exception, you could do so too: this.nome = (nome != null) ? nome : "";

  • It is a good practice to make these comparisons in the constructor, IE, is it common to think that I am not using JPA? If you had used JPA it would only be done with @annotation in the right attribute?

  • 1

    @JGSILVA In the vc constructor you can do the validation that is necessary so that the object does not receive invalid data. There is no such thing as "good practice", there is what makes the most sense in each case. Does the business rule say that the name cannot be null (or empty, or has to have at least X characters, etc)? Then validate without fear :-)

  • @JGSILVA Recommended reading: What good is a builder?

  • 1

    @JGSILVA Just to conclude (because it is not the focus of the question) I prefer the approach of Piovezan (if the name is invalid, throws exception and does not let create the object). Leaving someone with an empty name seems strange to me, does it make sense to have someone without a name? (Of course it will depend on the requirements, but anyway, I will not extend anymore)

  • Thank you, I fully understood your answer above and I have implemented it. I understood even more about the link you sent, that we can by validation in the constructor and this was my question, I’ve seen in the link that we can put yes and in your reply. What I meant by JPA (using Hibernate actually), is that I could put the validation in the name attribute (to prevent it from being blank) through the @Notnull annotation and just not there anymore in the constructor using the Piovezan approach. But as you said this escaping from the main question, I keep asking in the course of studies... Thank you all

  • @JGSILVA If the answer solved your problem, you can accept it, see here how and why to do it. It is not mandatory, but it is a good practice of the site, to indicate to future visitors that it solved the problem. Don’t forget that you can also vote in response, if it has found it useful.

  • Answer accepted :)

Show 4 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.