Sort string containing letters and numbers

Asked

Viewed 626 times

1

I have a list of objects that has an integer and a String (composed of letters and numbers). I would like to sort by that integer and as a second clause the alphabetical/numerical order of the string, so I tried to use the method sort class Collections.

Map class:

public class Mapa {
    private Integer sequencia;
    private String lote;

    //get e set
    @Override
    public String toString() {
        return "Mapa [sequencia=" + sequencia + ", lote=" + lote + "]";
    }
}

Test class:

public class App {

    public static void main(String[] args) throws IOException {
        List<Mapa> mapas = new ArrayList<Mapa>();

        for (int i = 50; i > 0; i--) {
            Mapa mapa = new Mapa();
            mapa.setSequencia(1);
            mapa.setLote("Lote 00"+i);
            mapas.add(mapa);
        }

        Collections.sort(mapas, new Comparator<Mapa>() {

            public int compare(Mapa arg0, Mapa arg1) {
                Integer numSeqArg0 = arg0.getSequencia();
                String loteArg0 = arg0.getLote();

                Integer numSeqArg1 = arg1.getSequencia();
                String loteArg1 = arg1.getLote();

                Integer compareNumSequencia = numSeqArg0.compareTo(numSeqArg1);
                Integer compareLote = loteArg0.compareTo(loteArg1);

                return compareNumSequencia == 0 ? compareLote : compareNumSequencia;
            }
        });

        for (Mapa mapa : mapas) {
            System.out.println(mapa);
        }
    }
}

Upshot:

Mapa [sequencia=1, lote=Lote 1]
Mapa [sequencia=1, lote=Lote 10]
Mapa [sequencia=1, lote=Lote 11]
Mapa [sequencia=1, lote=Lote 12]
Mapa [sequencia=1, lote=Lote 13]
Mapa [sequencia=1, lote=Lote 14]
Mapa [sequencia=1, lote=Lote 15]
Mapa [sequencia=1, lote=Lote 16]
Mapa [sequencia=1, lote=Lote 17]
Mapa [sequencia=1, lote=Lote 18]
Mapa [sequencia=1, lote=Lote 19]
Mapa [sequencia=1, lote=Lote 2]
Mapa [sequencia=1, lote=Lote 20]
Mapa [sequencia=1, lote=Lote 21]
Mapa [sequencia=1, lote=Lote 22]
Mapa [sequencia=1, lote=Lote 23]
Mapa [sequencia=1, lote=Lote 24]
Mapa [sequencia=1, lote=Lote 25]
Mapa [sequencia=1, lote=Lote 26]
Mapa [sequencia=1, lote=Lote 27]
Mapa [sequencia=1, lote=Lote 28]
Mapa [sequencia=1, lote=Lote 29]
Mapa [sequencia=1, lote=Lote 3]
Mapa [sequencia=1, lote=Lote 30]
Mapa [sequencia=1, lote=Lote 31]
Mapa [sequencia=1, lote=Lote 32]
Mapa [sequencia=1, lote=Lote 33]
Mapa [sequencia=1, lote=Lote 34]
Mapa [sequencia=1, lote=Lote 35]
Mapa [sequencia=1, lote=Lote 36]
Mapa [sequencia=1, lote=Lote 37]
Mapa [sequencia=1, lote=Lote 38]
Mapa [sequencia=1, lote=Lote 39]
Mapa [sequencia=1, lote=Lote 4]
Mapa [sequencia=1, lote=Lote 40]
Mapa [sequencia=1, lote=Lote 41]
Mapa [sequencia=1, lote=Lote 42]
Mapa [sequencia=1, lote=Lote 43]
Mapa [sequencia=1, lote=Lote 44]
Mapa [sequencia=1, lote=Lote 45]
Mapa [sequencia=1, lote=Lote 46]
Mapa [sequencia=1, lote=Lote 47]
Mapa [sequencia=1, lote=Lote 48]
Mapa [sequencia=1, lote=Lote 49]
Mapa [sequencia=1, lote=Lote 5]
Mapa [sequencia=1, lote=Lote 50]
Mapa [sequencia=1, lote=Lote 6]
Mapa [sequencia=1, lote=Lote 7]
Mapa [sequencia=1, lote=Lote 8]
Mapa [sequencia=1, lote=Lote 9]

As we can see, the string is getting sorted incorrectly. How can I sort a string in alphabetical order, which also obeys the numbers that compose it?

  • The title of the question does not reflect the doubt itself. I suggest rewriting to something clearer like "sort ArrayList of object by int and String

1 answer

2


When you compare strings, even the digits are compared taking into account the lexicographical order of the characters (i.e., their numerical value is not taken into account, instead a comparison is made between the value of the code points Unicode of characters).

In the same way that "abacate" comes before "abelha" (because the first and second letters are the same, but the third makes the "tiebreaker"), "Lote 40" comes before "Lote 5", because the first 5 characters are equal ("Lot" and the space), and the character 4 makes the tiebreaker (because lexicographically it comes before the 5).

If you want to compare the string and its numeric value, this should be done separately. You can use split to break the string into two parts, compare the first ("Lot" ) as string and the second as number:

Collections.sort(mapas, new Comparator<Mapa>() {
    public int compare(Mapa arg0, Mapa arg1) {
        Integer numSeqArg0 = arg0.getSequencia();
        Integer numSeqArg1 = arg1.getSequencia();
        int compareNumSequencia = numSeqArg0.compareTo(numSeqArg1);
        if (compareNumSequencia != 0) {
            // se número é diferente, não precisa comparar os lotes
            return compareNumSequencia;
        }
        String[] lote0Partes = arg0.getLote().split(" ");
        String[] lote1Partes = arg1.getLote().split(" ");

        int compareLote = lote0Partes[0].compareTo(lote1Partes[0]);
        int compareLoteNum = Integer.parseInt(lote0Partes[1]) - Integer.parseInt(lote1Partes[1]);

        return compareLote == 0 ? compareLoteNum : compareLote;
    }
});

The split separates the string by spaces, so the return is an array containing two strings: Lote and its number. Then I compare the first part, and if they are equal I use Integer.parseInt to turn the second part into a number and compare according to the numerical values.

Also note that the methods compareTo return a int, then you don’t need to assign the return to a Integer (Java does the auto-Boxing automatically, but in this case we will only compare the numerical values later, then you can use int directly).

Browser other questions tagged

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