Este es un post rápido para mostrar como se hace en Java para ordenar un HashMap por las llaves de sus elementos.

Lo haremos utilizando lo siguientes métodos:

  • TreeMap
  • ArrayList y Collections.sort()
  • TreeSet
  • Usando el Stream API
  • Usando la librería Guava

TreeMap

Como sabemos las llaves en un TreeMap están ordenadas usando su orden natural. Esto es una buena solución cuando queremos ordenar los pares por sus llaves. La idea es insertar toda la data de nuestro HashMap en un TreeMap.

Como primer asunto vamos a crear un HashMap

Map<String, Empleado> map = new HashMap<>();
 
Empleado empleado1 = new Empleado(1L, "Maria");
map.put(empleado1.getNombre(), empleado1);

Empleado empleado2 = new Empleado(22L, "Ana");
map.put(empleado2.getNombre(), empleado2);

Empleado empleado3 = new Empleado(8L, "Juan");
map.put(empleado3.getNombre(), empleado3);

Empleado empleado4 = new Empleado(2L, "Jorge");
map.put(empleado4.getNombre(), empleado4);

Y también implementamos la clase Empleado, que hereda de Comparable

public class Empleado implements Comparable<Empleado> {
 
    private Long id;
    private String nombre;

    public Empleado(final Long id, final String nombre) {
        this.id = id;
        this.nombre = nombre;
    }

    public Long getId() {
        return id;
    }

    public void setId(final Long id) {
        this.id = id;
    }

    public String getNombre() {
        return nombre;
    }

    public String setNombre(final String nombre) {
        this.nombre = nombre;
    }
 
    @Override
    public int compareTo(Empleado empleado) {
        return (int)(this.id - empleado.getId());
    }
}

Ahora simplemente le pasamos los elementos a un TreeMap en su constructor:

TreeMap<String, Empleado> ordenado = new TreeMap<>(map);

O bien, con el método putAll

TreeMap<String, Empleado> sorted = new TreeMap<>();
sorted.putAll(map);

Y listo, las entradas del TreeMap estarán automáticamente ordenadas en su orden natural:

Ana=Empleado{id=22, nombre='Ana'}
Jorge=Empleado{id=2, nombre='Jorge'}
Juan=Empleado{id=8, nombre='Juan'}
Maria=Empleado{id=1, nombre='Maria'}

ArrayList

Podríamos ordenar los elementos del HasMap en un ArrayList fácilmente pasando los valores a un nuevo ArrayList y luego ordenándolo. Pero perderemos la interfaz del mapa.

Ordenarlo por sus llaves:

List<String> empleados = new ArrayList<>(map.keySet());
Collections.sort(empleados);

Ordenarlo por sus valores:

List<String> empleados = new ArrayList<>(map.values()); 
Collections.sort(empleados);

Lo anterior funciona porque la clase empleados implementa la interfaz Comparable.

TreeSet

Similarmente a como lo hicimos con el ArrayList pero con TreeSet

Por llaves:

SortedSet<String> llaves = new TreeSet<>(map.keySet());

Por valores:

SortedSet<Employee> valores = new TreeSet<>(map.values());

En este caso no es necesario llamar al Collections.sort como en ArrayList, porque los TreeSet también están ordenados por defecto.

Usando Streams

A partir de Java 8 podemos usar la magia de la programación funcional y valernos de los streams y el método comparingByKey.

map.entrySet()
  .stream()
  .sorted(Map.Entry.<String, Empleado>comparingByKey())
  .forEach(System.out::println);

Por defecto el orden es ascendente:

Ana=Employee{id=22, nombre='Ana'}
Jorge=Employee{id=2, nombre='Jorge'}
Juan=Employee{id=8, nombre='Juan'}
Maria=Employee{id=1, nombre='Maria'}

También lo podemos hacer por valor:

map.entrySet()
  .stream()
  .sorted(Map.Entry.comparingByValue())
  .forEach(System.out::println);

Y también podemos usar collect para trasladar los elementos ordenados a un nuevo HashMap

Map<String, Employee> resultado = map.entrySet()
  .stream()
  .sorted(Map.Entry.comparingByValue())
  .collect(Collectors.toMap(
    Map.Entry::getKey, 
    Map.Entry::getValue, 
    (viejo, nuevo) -> viejo, LinkedHashMap::new));

Note que colectamos los resultados en un LinkedHashMap. Esto porque por defecto Collectors.toMap regresa un nuevo HashMap, pero como sabemos un HasMap no nos garantiza el orden de la iteración, mientras que un LinkedHashMap si.

Guava

Con la librería guava Es posible hacer esto mas fácil aun! haciendo uso de la clase Ordering Y luego de la clase InmutableSortedMap

Ordering naturalOrdering = Ordering.natural()
  .onResultOf(Functions.forMap(map, null));

ImmutableSortedMap.copyOf(map, naturalOrdering);

Ordering.onResultOf regresa un nuevo orden F a cuyos elementos primero se les aplicó la función indiciada, y despues se compararon usando this.

Y una vez mas la salida es el mapa ordenado por su campo Id

Maria=Empleado{id=1, nombre='Maria'}
Jorge=Empleado{id=2, nombre='Jorge'}
Juan=Empleado{id=8, nombre='Juan'}
Ana=Empleado{id=22, nombre='Ana'}

Categorized in: