Introducción
En este tutorial rápido, aprenderemos cómo ordenar un HashMap en Java. Más específicamente, examinaremos cómo ordenar las entradas de un HashMap por sus claves o valores utilizando diversas técnicas:
- TreeMap
- ArrayList y Collections.sort()
- TreeSet
- Usando la Stream API
- Usando la biblioteca Guava
1. Usando un TreeMap
Como sabemos, las claves en un TreeMap se ordenan según su orden natural. Esta es una buena solución cuando queremos ordenar los pares clave-valor por su clave. La idea es transferir todos los datos de nuestro HashMap a un TreeMap.
Primero, definamos un HashMap e inicialicémoslo con algunos datos:
Map<String, Employee> map = new HashMap<>();
Employee employee1 = new Employee(1L, "Mher");
map.put(employee1.getName(), employee1);
Employee employee2 = new Employee(22L, "Annie");
map.put(employee2.getName(), employee2);
Employee employee3 = new Employee(8L, "John");
map.put(employee3.getName(), employee3);
Employee employee4 = new Employee(2L, "George");
map.put(employee4.getName(), employee4);
Para la clase Employee, tenemos en cuenta que implementamos Comparable:
public class Employee implements Comparable<Employee> {
private Long id;
private String name;
public Employee(Long id, String name) {
this.id = id;
this.name = name;
}
// Getters y Setters
public Long getId() {
return id;
}
public String getName() {
return name;
}
// Sobrescribir equals y hashCode
@Override
public int compareTo(Employee employee) {
return (int)(this.id - employee.getId());
}
}
A continuación, almacenamos las entradas en el TreeMap utilizando su constructor:
TreeMap<String, Employee> sorted = new TreeMap<>(map);
También podemos usar el método putAll para copiar los datos:
TreeMap<String, Employee> sorted = new TreeMap<>();
sorted.putAll(map);
¡Y eso es todo! Para asegurarnos de que nuestras entradas de mapa están ordenadas por clave, ¡imprimamos los resultados!
Annie=Employee{id=22, name='Annie'}
George=Employee{id=2, name='George'}
John=Employee{id=8, name='John'}
Mher=Employee{id=1, name='Mher'}
Como podemos ver, las claves están ordenadas en orden natural.
2. Usando ArrayList
Por supuesto, también podemos ordenar las entradas del mapa con la ayuda de un ArrayList. La diferencia clave con el método anterior es que no mantenemos la interfaz Map aquí.
2.1. Ordenar por Clave
Primero, carguemos el conjunto de claves en un ArrayList:
List<String> employeeByKey = new ArrayList<>(map.keySet());
Collections.sort(employeeByKey);
El resultado es:
[Annie, George, John, Mher]
2.2. Ordenar por Valor
Ahora, ¿qué pasa si queremos ordenar nuestros valores de mapa por el campo id del objeto Employee? También podemos usar un ArrayList para eso.
Primero, copiemos los valores en la lista:
List<Employee> employeeById = new ArrayList<>(map.values());
Luego, lo ordenamos:
Collections.sort(employeeById);
Recuerda que esto funciona porque el Employee implementa la interfaz Comparable. De lo contrario, necesitaríamos definir un comparador manual para nuestra llamada a Collections.sort.
Para verificar los resultados, imprimimos el employeeById:
[Employee{id=1, name='Mher'},
Employee{id=2, name='George'},
Employee{id=8, name='John'},
Employee{id=22, name='Annie'}]
Como podemos ver, los objetos están ordenados por su campo id.
3. Usando un TreeSet
Si no queremos aceptar valores duplicados en nuestra colección ordenada, hay una buena solución con TreeSet.
Primero, añadamos algunas entradas duplicadas a nuestro mapa inicial:
Employee employee5 = new Employee(1L, "Mher");
map.put(employee5.getName(), employee5);
Employee employee6 = new Employee(22L, "Annie");
map.put(employee6.getName(), employee6);
3.1. Ordenar por Clave
Para ordenar el mapa por sus entradas de clave:
SortedSet<String> keySet = new TreeSet<>(map.keySet());
Imprimimos el keySet y vemos la salida:
[Annie, George, John, Mher]
Ahora tenemos las claves del mapa ordenadas sin duplicados.
3.2. Ordenar por Valor
Del mismo modo, para los valores del mapa, el código de conversión se ve así:
SortedSet<Employee> values = new TreeSet<>(map.values());
Y los resultados son:
[Employee{id=1, name='Mher'},
Employee{id=2, name='George'},
Employee{id=8, name='John'},
Employee{id=22, name='Annie'}]
Como podemos ver, no hay duplicados en la salida. Esto funciona con objetos personalizados cuando sobrescribimos equals y hashCode.
4. Usando Lambdas y Streams
Desde Java 8, podemos utilizar la Stream API y expresiones lambda para ordenar el mapa. Todo lo que necesitamos es llamar al método sorted en el pipeline de stream del mapa.
4.1. Ordenar por Clave
Para ordenar por clave, usamos el comparador comparingByKey:
map.entrySet()
.stream()
.sorted(Map.Entry.<String, Employee>comparingByKey())
.forEach(System.out::println);
La etapa final forEach imprime los resultados:
Annie=Employee{id=22, name='Annie'}
George=Employee{id=2, name='George'}
John=Employee{id=8, name='John'}
Mher=Employee{id=1, name='Mher'}
Por defecto, el modo de ordenación es ascendente.
4.2. Ordenar por Valor
Por supuesto, también podemos ordenar por los objetos de Employee:
map.entrySet()
.stream()
.sorted(Map.Entry.comparingByValue())
.forEach(System.out::println);
Como podemos ver, el código anterior imprime un mapa ordenado por los campos id de los objetos Employee:
Mher=Employee{id=1, name='Mher'}
George=Employee{id=2, name='George'}
John=Employee{id=8, name='John'}
Annie=Employee{id=22, name='Annie'}
Además, podemos recopilar los resultados en un nuevo mapa:
Map<String, Employee> result = map.entrySet()
.stream()
.sorted(Map.Entry.comparingByValue())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(oldValue, newValue) -> oldValue, LinkedHashMap::new));
Ten en cuenta que recopilamos nuestros resultados en un LinkedHashMap. Por defecto, Collectors.toMap devuelve un nuevo HashMap, pero como sabemos, HashMap no garantiza el orden de inserción, mientras que LinkedHashMap sí.
5. Usando Guava
Por último, una biblioteca que nos permite ordenar el HashMap es Guava. Antes de comenzar, será útil revisar nuestro artículo sobre mapas en Guava.
Primero, declaremos un *Ordering*, ya que queremos ordenar nuestro mapa por el campo Id del Employee:
Ordering<Employee> naturalOrdering = Ordering.natural()
.onResultOf(Functions.forMap(map, null));
Ahora, todo lo que necesitamos es usar ImmutableSortedMap para mostrar los resultados:
ImmutableSortedMap.copyOf(map, naturalOrdering);
Y una vez más, la salida es un mapa ordenado por el campo id:
Mher=Employee{id=1, name='Mher'}
George=Employee{id=2, name='George'}
John=Employee{id=8, name='John'}
Annie=Employee{id=22, name='Annie'}
Conclusión
En este artículo, revisamos varias maneras de ordenar un HashMap por clave o valor. Aprendimos cómo hacer esto implementando Comparable cuando el atributo es una clase personalizada.
Consejos Prácticos
- Selecciona el método de ordenación adecuado: Dependiendo del tamaño de tu HashMap y del criterio de ordenación, elige entre TreeMap, ArrayList, o las Streams de Java.
- Implementa Comparable: Cuando trabajes con objetos personalizados, asegúrate de implementar la interfaz Comparable correctamente para facilitar la ordenación.
- Considera la duplicidad: Si tus datos pueden contener duplicados, recuerda usar TreeSet o validar los valores antes de insertar en tu colección.
- Usa bibliotecas externas si es necesario: La biblioteca Guava ofrece métodos eficientes de manipulación de mapas, lo que puede ahorrarte tiempo y esfuerzo.
Con esto, ahora tienes una forma sólida y versátil de manejar la ordenación de HashMap en Java.