Ordenar listas con Stream en Java
El tratamiento de listas actualmente en Java desde la versión de Java 8 suele pasar por el uso de Streams. En este breve artículo vamos a ver como ordenar listas con Stream en Java.
¿Qué es un Stream de Java?
El API Stream de Java que fue introducida desde Java 8, nos permite procesar y realizar operaciones con colecciones de objetos. Un Stream es una secuencia de objetos que soporta varios métodos en una secuencia para obtener el resultado deseado.
¿Cómo ordenar una lista con Stream en Java?
Dentro de los stream de Java tenemos la interfaz sorted que nos va a permitir realizar ordenaciones sobre el stream.
Stream<T> sorted()
En donde stream es una interfaz y T es el tipo de objeto de nuestra colección.
Este método devuelve un stream que consiste en el tipo del elemento del stream en donde al aplicar sorted se aplicará una ordenación natural. En el caso en el que a los elementos del stream no se les pueda aplicar Comparable se lanzará una excepción del tipo ClassCastException.
Ordenar una lista de enteros con Stream en Java
Vamos a aplicar el metodo sort para ordenar listas de integers, para ello vamos a comenzar creando una lista:
List<Integer> integers = List.of(11,2,19,3,4);
Ahora vamos a realizar la ordenación natural y ver su salida:
integers.stream() .sorted() .forEach(System.out::println);
Y el resultado sería:
2 3 4 11 19
Si queremos podemos almacenar el resultado en una nueva lista:
List<Integer> sortedInteger = integers.stream() .sorted() .collect(Collectors.toList());
Ordenar lista de enteros de manera descendente
El uso de sorted() en un stream va a ordenar nuestra lista con un orden natural, es decir, de manera ascendente. Para poder aplicar una ordenación de forma descendente habrá que hacer uso de Collections.reverseOrder(), como parámetro de sorted.
Por ejemplo, vamos a ordenar la siguiente lista de enteros de manera descendente:
List<Integer> integers = List.of(11,2,19,3,4); List<Integer> sortedInteger = integers.stream() .sorted(Collections.reverseOrder()) .collect(Collectors.toList()); System.out.println(sortedInteger);
El resultado de la ejecución anterior sería:
[19,11,4,3,2]
Ordenar lista de String en un stream de Java
Cuando queremos ordenar una lista de Strings en Java, podemos hacer uso de sorted(), el cual nos ordenará de manera lexicográfica la lista.
Vamos a ver un ejemplo:
List<String> stringList = List.of("Alba","Noel","Pablo","Alejandro");
Vamos a ejecutar el siguiente comando:
List<String> sortedList = list.stream().sorted().collect(Collectors.toList()); System.out.println(sortedList);
El sorted anterior realizará una ordenación lexicográfica de la siguiente manera:
[Alba,Alejandro,Noel,Pablo]
Y al igual que hemos hecho anteriormente vamos a ver como devolver ordenada a la inversa una lista de strings.
Ordenar lista de String de forma descendente
Al igual que hemos hecho para ordenar una lista de números enteros de manera inversa on Collections.reverseOrder(), vamos a aplicar la misma función a nuestra lista de Strings.
List<String> stringList = List.of("Alba","Noel","Pablo","Alejandro"); List<String> sortedList = list.stream().sorted(Collections.reverseOrder()).collect(Collectors.toList()); System.out.println(sortedList);
Pero en algunas ocasiones vamos a necesitar ordenar objetos propios complejos, vamos a verlo en los siguientes puntos:
Ordenar Objetos propios con Java Stream
En los ejemplos anteriores hemos hecho uso de tipos «Comparables», pero qué sucede cuando tenemos objetos propios?, pues podemos hacer uso de Comparator dentro de sorted.
Vamos a ver un ejemplo con un tipo de objeto User:
@Getter @Setter @AllArgsConstructor public class User { private String name; private int age; }
Tenemos una lista de objetos de tipo User y queremos que esa lista sea ordenada por la edad de manera natural. Vamos a ver un ejemplo:
List<User> userList = List.of( new User("Noel", 39), new User("Pablo", 1), new User("Gusi", 38)); List<User> sortedList = userList.stream() .sorted(Comparator.comparingInt(User::getAge)) .collect(Collectors.toList()); sortedList.forEach(System.out::println);
Al ejecutar
User:[name: Pablo, age: 1] User:[name: Gusi, age: 38] User:[name: Noel, age: 39]
En el ejemplo anterior hemos filtrado por el campo age del objeto User haciendo uso de la clase Comparator del método comparingInt:
static <T> Comparator<T> comparingInt(ToIntFunction <T> keyExtractor)
Al igual que en los casos anteriores, podemos obtener el resultado de la lista de manera inversa haciendo uso de reversed().
List<User> userList = List.of( new User("Noel", 39), new User("Pablo", 1), new User("Gusi", 38)); List<User> sortedList = userList.stream() .sorted(Comparator.comparingInt(User::getAge).reversed()) .collect(Collectors.toList()); sortedList.forEach(System.out::println);
Y obviamente el resultado anterior será la lista de usuarios en orden inverso al natural:
User:[name: Noel, age: 39] User:[name: Gusi, age: 38] User:[name: Pablo, age: 1]
Como información a tener en cuenta, si dos User tuviesen el mismo age(), quedaría ordenado según la posición que ocupen en la lista. Si queremos algún tipo de ordenación adicional habría que crear un custom comparator, lo cual veremos en el próximo punto.
Crear un Custom Comparator para utilizar en Stream.sorted()
En algunas ocasiones vamos a querer realizar comparaciones por algo más complejo que únicamente hacer uso de comparaciones de enteros o string.
Por ejemplo, en el caso anterior si dos usuarios tienen la misma edad serán ordenados en función de la posición de la lista, pero y si queremos, para esas ocasiones, tener en cuenta el nombre?.
Vamos a ver como podemos crear un Comparator custom para ordenar según nuestras necesidades.
Vamos a partir del ejemplo anterior:
List<User> userList = List.of( new User("Noel", 39), new User("Pablo", 1), new User("Gusi", 38));
Y ahora vamos a crear un método que si dos usuarios tienen la misma edad compara los nombres:
private Comparator<User> userComparator() { return new Comparator<User>() { @Override public int compare(User user1, User user2) { if(user1.getAge() == user2.getAge()) return user1.getName().compareTo(user2.getName()); else if(user1.getAge() > user2.getAge()) return 1; else return -1; } }; }
En el método anterior lo que hemos hecho ha sido ordenar, para cuando dos usuarios tienen la misma edad, por nombre. Ahora vamos a aplicar el método anterior al código de ordenación:
List<User> sortedList = userList.stream() .sorted(userComparator) .collect(Collectors.toList());
De esta manera nuestra función lambda tendrá los usuarios ordenados por la edad y el nombre.
Conclusión
En esta entrada hemos visto como podemos Ordenar listas con Stream en Java haciendo uso de las funciones predefinidas de Java y creando nuestro propio método para realizar las comparaciones.
El entender como funciona Comparator nos va a ayudar a crear nuestras propias ordenaciones.
Otros artículos que quizás te interesen pueden ser:
- Stream filter() en Java
- Java Supplier y patrón factoría
- Usando Streams con Mapas de Java
- Reduce en Java Stream con Ejemplos
Si necesitas más información puedes escribirnos un comentario o un correo electrónico a refactorizando.web@gmail.com o también nos puedes contactar por nuestras redes sociales Facebook o twitter y te ayudaremos encantados!