La forma correcta de ordenar alfabéticamente cadenas en Java

Escrito por el , actualizado el .
java planeta-codigo programacion
Enlace permanente Comentarios

Sin usar la clase Collator incluida en el JDK al ordenar alfabéticamente una lista de cadenas obtendremos en algún caso un resultado que nos extrañará y seguramente no sea lo esperado. La clase String implementa la interfaz Comprable pero esta ordenación es en base al valor del código unicode sin tener en cuenta los diferentes niveles de diferencias propios del idioma para algunos caracteres de la cadena como tildes o caracteres únicos como la ñ. La forma correcta de ordenar cadenas en Java es utilizando la clase Collator.

Java

Quizá algunos digan que la aparentemente sencilla tarea de ordenar una lista de palabras es algo fácil de hacer con cualquier lenguaje de programación. La realidad es que puede complicarse en una buena cantidad si se ha de realizar con los caracteres del alfabeto de algunos lenguajes. La ordenación no es tan simple como realizar lo siguiente en código Java:

1
2
String[] cadenas = new String[] { "Cantabria", "Álava", "Alava", "alava" };
Arrays.sort(cadenas);
Sort.java

Primeramente nos daremos cuenta de que la siguiente lista de nombres de provincias las ordena de una forma que quizá no es la que esperamos. En algunas páginas web se puede observar este error en algunos elementos de selección de opciones de nombres de países, ciudades, provincias u otro conjunto de elementos que suelen estar ordenados de forma alfabética para facilitar el encontrar el elemento a seleccionar pero que si se da el caso de que hay variación de palabras con tildes y sin ellas y minúsculas y mayúsculas se presenta la ordenación incorrecta.

1
2
Alava, Cantabria, alava, Álava

Sort.out

Como se observa las palabras con letras mayúsculas se ordenan antes que las palabras con letras en minúscula independientemente de la letra del alfabeto, seguramente esta no es la ordenación deseada. En algunos lenguajes como el español algo similar ocurre con las palabras que llevan tilde en alguna letra. Convertir las palabras a mayúsculas o minúsculas o eliminar las tildes por los mismos sin tilde previamente a hacer la ordenación además de no ser una buena solución no sirve para otros lenguajes con diferentes formas de tilde y marcas en las letras.

En Java la solución es utilizar la clase Collator, esta clase establece varios niveles en las que las letras se consideran diferentes. Por ejemplo, en español las letras e y f se consideran diferencias primarias (diferentes letras), e y é son diferencias secundarias (diferentes tildes) y e y E son diferencias terciarias (diferencias entre mayúsculas y minúsculas). Las diferencias entre caracteres dependen del Locale y un Collator se obtiene en base a él con el método estático getInstance​(Locale). Con el método setStrength(int) se establece el nivel de diferencias deseadas.

Usando la clase Collator y realizando la ordenación de la misma lista anterior el resultado es diferente y seguramente más apropiado. Como la clase Collator implementa la interfaz Comparable podemos usarla como el comparador aplicar en el método Arrays.sort(T[], Comparator<? super T>) o List.sort(Comparator<? super E>).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package io.github.picodotodev.blogbitix.javacollator;

import java.text.Collator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;

public class Main {

    @SuppressWarnings("unchecked")
    public static void main(String[] args) {
        List cadenas = List.of("Cantabria", "Álava", "Alava", "alava");
        
        String[] sortedNoCollator = (String[]) cadenas.toArray(new String[0]);
        List sortedPrimaryCollator = new ArrayList(cadenas);
        List sortedSecondaryCollator = new ArrayList(cadenas);
        List sortedTertiaryCollator = new ArrayList(cadenas);

        Collator primaryCollator = Collator.getInstance(new Locale("es"));
        Collator secondaryCollator = Collator.getInstance(new Locale("es"));
        Collator tertiaryCollator = Collator.getInstance(new Locale("es"));

        primaryCollator.setStrength(Collator.PRIMARY);
        secondaryCollator.setStrength(Collator.SECONDARY);
        tertiaryCollator.setStrength(Collator.TERTIARY);

        Arrays.sort(sortedNoCollator);
        sortedPrimaryCollator.sort(primaryCollator);
        sortedSecondaryCollator.sort(secondaryCollator);
        sortedTertiaryCollator.sort(tertiaryCollator);

        System.out.printf("Lista cadenas:                             %s%n", cadenas);        
        System.out.printf("Ordenación sin clase Collator:             %s%n", Arrays.asList(sortedNoCollator));        
        System.out.printf("Ordenación con clase Collator (primary):   %s%n", sortedPrimaryCollator);
        System.out.printf("Ordenación con clase Collator (secondary): %s%n", sortedSecondaryCollator);
        System.out.printf("Ordenación con clase Collator (tertiary):  %s%n", sortedTertiaryCollator);
    }
}
Main.java
1
2
3
4
5
Lista cadenas:                             [Cantabria, Álava, Alava, alava]
Ordenación sin clase Collator:             [Alava, Cantabria, alava, Álava]
Ordenación con clase Collator (primary):   [Álava, Alava, alava, Cantabria]
Ordenación con clase Collator (secondary): [Alava, alava, Álava, Cantabria]
Ordenación con clase Collator (tertiary):  [alava, Alava, Álava, Cantabria]
System.out

Utilizando el Collator con solo diferencias primarias Cantabria se ordena al final de la lista por tener las letras a, A y Á una diferencia primaria con C. Con diferencias secundarias las letras A y a se ordenan antes que Á por tener diferencias secundarias. Finalmente, con diferencias terciarias a se ordena antes que A.

Otras funcionalidades que dependen del locale utilizadas en la mayoría de los programas son Internacionalizar y localizar cadenas, números, importes y fechas en Java.

Terminal

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando siguiente comando:
./gradlew run


Comparte el artículo: