API de Stream en Java
Existen diversas formas de procesar datos en Java: bucles, métodos y diferentes algoritmos. Sin embargo, en Java 8 se introdujo una herramienta muy potente: la Stream API.
En términos simples, la Stream API es una forma de trabajar de manera rápida y sencilla con un flujo de información. En nuestro caso, este flujo de información está representado por colecciones. La Stream API tiene algunos conceptos. Aquí están los principales.
Conceptos principales
-
Stream: representa una secuencia de elementos de datos que pueden ser procesados;
-
Operaciones intermedias: operaciones que crean un nuevo stream después de su ejecución. Ejemplos:
filter,map,distinct,sorted; -
Operaciones terminales: operaciones que completan el procesamiento del stream y devuelven un resultado. Ejemplos:
collect,forEach,count,reduce; -
Streams paralelos: permiten el procesamiento paralelo de datos. Los métodos
parallel()yparallelStream()se utilizan para crear streams paralelos.
Suficiente teoría, ¡comencemos a programar!
La declaración de un stream se realiza utilizando un método sobre la colección que se desea convertir en un stream:
Main.java
12List<String> strings = Arrays.asList("a", "b", "c"); Stream<String> stream = strings.stream();
Con el método stream(), se obtiene un stream de cadenas. Sin embargo, para comenzar a trabajar con el stream, es necesario comprender qué son las expresiones lambda, ya que los métodos de stream funcionan principalmente con ellas.
Expresiones Lambda
Las expresiones lambda se introdujeron en Java 8 y representan una forma simplificada de crear funciones anónimas en Java. No hemos tratado las funciones anónimas anteriormente ya que no eran muy necesarias, pero ahora las conoceremos a través de las expresiones lambda.
Sintaxis de las expresiones lambda:
La sintaxis general para las expresiones lambda en Java es la siguiente:
Example.java
123(parameters) -> expression // or (parameters) -> { statements; }
-
Parámetros: esta es una lista de parámetros que puede estar vacía o contener uno o más parámetros;
-
Flecha: representada por el símbolo
->, que separa los parámetros del cuerpo de la expresión lambda; -
Expresión o sentencias: este es el cuerpo de la función, que contiene una expresión o un bloque de sentencias.
Aquí tienes un ejemplo de una expresión lambda que representa una función simple que suma dos números:
Example.java
12345678910// Traditional way MathOperation addition = new MathOperation() { @Override public int operate(int a, int b) { return a + b; } }; // Using a lambda expression MathOperation addition = (int a, int b) -> a + b;
Analicemos más de cerca qué está ocurriendo exactamente en el código anterior y cómo utilizamos las expresiones lambda:
Main.java
1234567891011121314package com.example; // Functional interface with a single abstract method interface MyMathOperation { int operate(int a, int b); } public class Main { public static void main(String[] args) { // Using a lambda expression to implement the interface MyMathOperation addition = (a, b) -> a + b; System.out.println("Sum: " + addition.operate(5, 3)); } }
En el código anterior:
Se creó una interfaz funcional MyMathOperation con un único método abstracto operate.
Se utilizó una expresión lambda para implementar este método, realizando la suma de dos números.
Se imprimió el resultado de la suma.
Entiendo que puede ser difícil de comprender lo que sucede en este código por ahora, pero volvamos a la Stream API, donde las expresiones lambda se utilizan con frecuencia, e intentemos entender cómo usarlas en la práctica.
Como recuerdas, anteriormente creamos un stream de cadenas a partir de una lista de cadenas. Ahora, utilicemos métodos de stream para convertir cada cadena de este stream en mayúsculas:
Main.java
12345678910111213package com.example; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("a", "b", "c"); Stream<String> stream = strings.stream(); stream.map(e -> e.toUpperCase()).toList(); } }
En el código anterior, se utilizó una expresión lambda y dos métodos: map() y toList(). Si bien es claro lo que hace el método toList(), el método map() modifica cada elemento en el stream de acuerdo con la expresión lambda proporcionada.
Analicemos más de cerca cómo funciona la expresión lambda en este caso:
El método map() aplica el método toUpperCase() a cada elemento del stream. Se definió el elemento de este stream como e y, utilizando la expresión lambda, se indicó al programa que aplicara este método a cada elemento.
Sin embargo, esto aún no finaliza el proceso, ya que se aplicó una operación intermedia. Esto significa que las operaciones sobre el stream aún no han finalizado. Para completar el trabajo sobre el stream, es necesario aplicar una operación terminal, la cual finalizará las operaciones sobre el stream y devolverá un valor específico. Por ejemplo, se puede utilizar el método toList(), y el stream modificado será convertido en una lista.
Por ejemplo:
Main.java
1234567891011121314package com.example; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("a", "b", "c"); Stream<String> stream = strings.stream(); List<String> list = stream.map(e -> e.toUpperCase()).toList(); System.out.println(list); } }
Analicemos más de cerca las posibles operaciones intermedias en el stream.
Operaciones Intermedias
El método map() - ya está familiarizado con este método; realiza las operaciones especificadas por la expresión lambda en cada elemento del stream.
Por ejemplo, utilicemos el método substring() en cada elemento de un stream de cadenas:
Main.java
123456789101112131415package com.example; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("Unlock", "Infinity", "with", "Codefinity"); System.out.println("List of strings: " + strings); Stream<String> stream = strings.stream(); List<String> list = stream.map(e -> e.substring(1, 4)).toList(); System.out.println("Modified list: " + list); } }
El método filter() recibe una expresión lambda con una condición según la cual se filtrará el stream. Es decir, todos los elementos que cumplan la condición permanecerán en el stream, y los elementos que no cumplan la condición serán eliminados del stream. Modifiquemos el stream para conservar solo los elementos cuya longitud sea mayor que 5:
Main.java
12345678910111213141516package com.example; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("Unlock", "Infinity", "with", "Codefinity"); System.out.println("List of strings: " + strings); Stream<String> stream = strings.stream(); stream = stream.filter(e -> e.length() > 5); List<String> list = stream.map(e -> e.substring(1, 4)).toList(); System.out.println("Modified list: " + list); } }
Usando el método filter(), eliminamos la cadena "with" del flujo porque esta palabra tiene menos de 5 caracteres.
También es posible utilizar operaciones intermedias varias veces de forma consecutiva.
Por ejemplo, podemos simplificar ligeramente el código anterior:
Main.java
12345678910111213141516package com.example; import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("Unlock", "Infinity", "with", "Codefinity"); System.out.println("List of strings: " + strings); List<String> list = strings.stream() .filter(e -> e.length() > 5) .map(e -> e.substring(1, 4)) .toList(); System.out.println("Modified list: " + list); } }
Al encadenar múltiples métodos de flujo juntos, se recomienda colocar cada método en una nueva línea para mejorar significativamente la legibilidad del código.
El método flatMap() transforma cada elemento de un stream en un nuevo stream y combina los resultados en un solo stream. En otras palabras, con este método, podemos dividir el stream en varios streams, y luego se fusionarán en un solo stream. Por ejemplo, tienes una lista de cadenas donde cada cadena puede contener más de una palabra, como una lista de nombres y apellidos. Y necesitas poner en mayúscula la primera letra de cada una de estas palabras:
Main.java
123456789101112131415161718192021222324package com.example; import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<String> users = Arrays.asList("Ethan Johnson", "Olivia smith", "mason davis", "Ava taylor", "logan brown", "Emma Anderson", "jackson miller"); System.out.println("List of users: " + users); List<String> list = users.stream() .flatMap(e -> Arrays.stream(e.split(" "))) .map(e -> capitalizeFirstLetter(e)) .toList(); System.out.println("List with capitalized names and surnames: " + list); } private static String capitalizeFirstLetter(String word) { if (word == null || word.isEmpty()) { return word; } return Character.toUpperCase(word.charAt(0)) + word.substring(1); } }
En el código anterior, escribimos un método privado separado que pone en mayúscula la primera letra de una palabra y utilizamos este método en el método map() junto con una expresión lambda.
Observa que al usar el método flatMap, dividimos cada elemento del stream en diferentes streams utilizando el método Arrays.stream(e.split(" ")). Debido a que el método split() devuelve un array, necesitamos usar el método Arrays.stream() para dividir este array en streams.
Luego, todos estos streams se fusionan en un solo stream, después de lo cual usamos el método que escribimos. Ahora tenemos todos los nombres y apellidos de usuarios con la primera letra en mayúscula.
¿Sabes qué sería interesante?
Si colocamos estos nombres y apellidos en un HashMap, donde la clave es el apellido y el valor es el nombre.
Vamos a implementarlo en el código:
Main.java
12345678910111213141516171819202122232425262728293031323334package com.example; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; public class Main { public static void main(String[] args) { List<String> users = Arrays.asList("Ethan Johnson", "Olivia smith", "mason davis", "Ava taylor", "logan brown", "Emma Anderson", "jackson miller"); System.out.println("List of users: " + users); List<String> list = users.stream() .flatMap(e -> Arrays.stream(e.split(" "))) .map(e -> capitalizeFirstLetter(e)) .toList(); System.out.println("List with capitalized names and surnames: " + list); Map<String, String> usersKeyValue = new HashMap<>(); for (int i = 0; i < list.size() - 1; i+=2) { String name = list.get(i); String surname = list.get(i + 1); usersKeyValue.put(surname, name); } System.out.println("Map with surnames as keys and names as values: " + usersKeyValue); } private static String capitalizeFirstLetter(String word) { if (word == null || word.isEmpty()) { return word; } return Character.toUpperCase(word.charAt(0)) + word.substring(1); } }
Con un bucle simple, almacenamos el nombre y el apellido en variables y luego en el mapa. Observe cómo funciona el bucle. Incrementamos la variable i en 2 en cada iteración porque necesitamos saltar el apellido una vez que ya lo hemos registrado.
- El método
distinct()elimina duplicados del stream. En general, esto puede ser útil si se necesitan elementos únicos en el stream o si se desea eliminar rápidamente duplicados de una lista. Esto se puede lograr fácilmente con la siguiente construcción:
list.stream().distinct().toList()
-
El método
sortedordena todos los elementos del stream en orden natural, desde el número más pequeño hasta el más grande o en orden alfabético. Esto también puede ser útil si se necesita un stream ordenado o si se requiere ordenar rápidamente una lista; -
El método
skip(n)omite los primerosnelementos del stream. Esto puede ser útil al trabajar con archivos de texto, donde las primeras n líneas pueden ser, por ejemplo, metadatos o una descripción del archivo. También es relevante mencionar el métodolimit(n), que generalmente limita la cantidad de elementos en el stream. Incluso si se crea un stream con 1000 elementos y luego se utilizalimit(200), el stream contendrá solo los primeros 200 elementos.
Main.java
123456789101112package com.example; import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<Integer> example = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); example = example.stream().skip(3).limit(5).toList(); System.out.println("List: " + example); } }
Estos son los principales métodos intermedios que necesitarás utilizar. Puedes explorar el resto de los métodos consultando la enlace a la documentación oficial de Java. Continuemos con los métodos terminales.
Métodos terminales
-
El método terminal con el que ya estás familiarizado es
toList(). Convierte el stream en una lista y la retorna. En otras palabras, puedes asignar directamente este stream con métodos a una lista. Este método fue introducido en Java 17 y reemplaza la construcción más complejacollect(Collectors.toList()); -
El método
collect()también convierte el stream en una estructura de datos específica. Utiliza, como parámetro, un método de la interfazCollectors. Esta interfaz contiene métodos comotoList(),toSet()ytoCollection(). Por ejemplo:
Main.java
123456789101112131415package com.example; import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.stream.Collectors; public class Main { public static void main(String[] args) { List<Integer> example = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); Set<Integer> integerSet = example.stream().collect(Collectors.toSet()); System.out.println("List: " + example); System.out.println("Set: " + integerSet); } }
El método forEach() recibe una expresión lambda y realiza una acción específica para cada elemento en el flujo.
Por ejemplo:
Main.java
1234567891011package com.example; import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<Integer> example = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); example.stream().forEach(e -> System.out.println(e + 1)); } }
La diferencia entre este método y el método map es que este método es terminal, y después de él, no se pueden llamar otros métodos.
Estos son todos los métodos básicos para trabajar con streams. Es un tema complejo, y puede que no lo comprendas de inmediato. Sin embargo, es un tema que se domina con la práctica. En los próximos capítulos prácticos sobre streams, tendrás muchas oportunidades para trabajar con ellos, ya que es una forma muy conveniente y práctica de manipular listas y arreglos de datos.
1. ¿Cuál es el propósito principal de la Stream API en Java?
2. ¿Cuál de las siguientes es una operación terminal en la Stream API?
3. ¿Qué hace la operación map en la API de Stream?
4. ¿En qué se diferencia la operación flatMap de map en la API de Stream?
5. ¿Qué hace la operación filter en la API de Stream?
6. ¿Cuál es el propósito de la operación forEach en la Stream API?
7. ¿Cuál de las siguientes es una operación intermedia en la Stream API?
8. ¿Cómo se utiliza la operación limit en la Stream API?
¡Gracias por tus comentarios!
Pregunte a AI
Pregunte a AI
Pregunte lo que quiera o pruebe una de las preguntas sugeridas para comenzar nuestra charla
Can you explain more about how lambda expressions work with streams?
What are some common use cases for the Stream API in real-world Java projects?
Could you provide more examples of intermediate and terminal operations?
Genial!
Completion tasa mejorada a 4
API de Stream en Java
Desliza para mostrar el menú
Existen diversas formas de procesar datos en Java: bucles, métodos y diferentes algoritmos. Sin embargo, en Java 8 se introdujo una herramienta muy potente: la Stream API.
En términos simples, la Stream API es una forma de trabajar de manera rápida y sencilla con un flujo de información. En nuestro caso, este flujo de información está representado por colecciones. La Stream API tiene algunos conceptos. Aquí están los principales.
Conceptos principales
-
Stream: representa una secuencia de elementos de datos que pueden ser procesados;
-
Operaciones intermedias: operaciones que crean un nuevo stream después de su ejecución. Ejemplos:
filter,map,distinct,sorted; -
Operaciones terminales: operaciones que completan el procesamiento del stream y devuelven un resultado. Ejemplos:
collect,forEach,count,reduce; -
Streams paralelos: permiten el procesamiento paralelo de datos. Los métodos
parallel()yparallelStream()se utilizan para crear streams paralelos.
Suficiente teoría, ¡comencemos a programar!
La declaración de un stream se realiza utilizando un método sobre la colección que se desea convertir en un stream:
Main.java
12List<String> strings = Arrays.asList("a", "b", "c"); Stream<String> stream = strings.stream();
Con el método stream(), se obtiene un stream de cadenas. Sin embargo, para comenzar a trabajar con el stream, es necesario comprender qué son las expresiones lambda, ya que los métodos de stream funcionan principalmente con ellas.
Expresiones Lambda
Las expresiones lambda se introdujeron en Java 8 y representan una forma simplificada de crear funciones anónimas en Java. No hemos tratado las funciones anónimas anteriormente ya que no eran muy necesarias, pero ahora las conoceremos a través de las expresiones lambda.
Sintaxis de las expresiones lambda:
La sintaxis general para las expresiones lambda en Java es la siguiente:
Example.java
123(parameters) -> expression // or (parameters) -> { statements; }
-
Parámetros: esta es una lista de parámetros que puede estar vacía o contener uno o más parámetros;
-
Flecha: representada por el símbolo
->, que separa los parámetros del cuerpo de la expresión lambda; -
Expresión o sentencias: este es el cuerpo de la función, que contiene una expresión o un bloque de sentencias.
Aquí tienes un ejemplo de una expresión lambda que representa una función simple que suma dos números:
Example.java
12345678910// Traditional way MathOperation addition = new MathOperation() { @Override public int operate(int a, int b) { return a + b; } }; // Using a lambda expression MathOperation addition = (int a, int b) -> a + b;
Analicemos más de cerca qué está ocurriendo exactamente en el código anterior y cómo utilizamos las expresiones lambda:
Main.java
1234567891011121314package com.example; // Functional interface with a single abstract method interface MyMathOperation { int operate(int a, int b); } public class Main { public static void main(String[] args) { // Using a lambda expression to implement the interface MyMathOperation addition = (a, b) -> a + b; System.out.println("Sum: " + addition.operate(5, 3)); } }
En el código anterior:
Se creó una interfaz funcional MyMathOperation con un único método abstracto operate.
Se utilizó una expresión lambda para implementar este método, realizando la suma de dos números.
Se imprimió el resultado de la suma.
Entiendo que puede ser difícil de comprender lo que sucede en este código por ahora, pero volvamos a la Stream API, donde las expresiones lambda se utilizan con frecuencia, e intentemos entender cómo usarlas en la práctica.
Como recuerdas, anteriormente creamos un stream de cadenas a partir de una lista de cadenas. Ahora, utilicemos métodos de stream para convertir cada cadena de este stream en mayúsculas:
Main.java
12345678910111213package com.example; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("a", "b", "c"); Stream<String> stream = strings.stream(); stream.map(e -> e.toUpperCase()).toList(); } }
En el código anterior, se utilizó una expresión lambda y dos métodos: map() y toList(). Si bien es claro lo que hace el método toList(), el método map() modifica cada elemento en el stream de acuerdo con la expresión lambda proporcionada.
Analicemos más de cerca cómo funciona la expresión lambda en este caso:
El método map() aplica el método toUpperCase() a cada elemento del stream. Se definió el elemento de este stream como e y, utilizando la expresión lambda, se indicó al programa que aplicara este método a cada elemento.
Sin embargo, esto aún no finaliza el proceso, ya que se aplicó una operación intermedia. Esto significa que las operaciones sobre el stream aún no han finalizado. Para completar el trabajo sobre el stream, es necesario aplicar una operación terminal, la cual finalizará las operaciones sobre el stream y devolverá un valor específico. Por ejemplo, se puede utilizar el método toList(), y el stream modificado será convertido en una lista.
Por ejemplo:
Main.java
1234567891011121314package com.example; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("a", "b", "c"); Stream<String> stream = strings.stream(); List<String> list = stream.map(e -> e.toUpperCase()).toList(); System.out.println(list); } }
Analicemos más de cerca las posibles operaciones intermedias en el stream.
Operaciones Intermedias
El método map() - ya está familiarizado con este método; realiza las operaciones especificadas por la expresión lambda en cada elemento del stream.
Por ejemplo, utilicemos el método substring() en cada elemento de un stream de cadenas:
Main.java
123456789101112131415package com.example; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("Unlock", "Infinity", "with", "Codefinity"); System.out.println("List of strings: " + strings); Stream<String> stream = strings.stream(); List<String> list = stream.map(e -> e.substring(1, 4)).toList(); System.out.println("Modified list: " + list); } }
El método filter() recibe una expresión lambda con una condición según la cual se filtrará el stream. Es decir, todos los elementos que cumplan la condición permanecerán en el stream, y los elementos que no cumplan la condición serán eliminados del stream. Modifiquemos el stream para conservar solo los elementos cuya longitud sea mayor que 5:
Main.java
12345678910111213141516package com.example; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("Unlock", "Infinity", "with", "Codefinity"); System.out.println("List of strings: " + strings); Stream<String> stream = strings.stream(); stream = stream.filter(e -> e.length() > 5); List<String> list = stream.map(e -> e.substring(1, 4)).toList(); System.out.println("Modified list: " + list); } }
Usando el método filter(), eliminamos la cadena "with" del flujo porque esta palabra tiene menos de 5 caracteres.
También es posible utilizar operaciones intermedias varias veces de forma consecutiva.
Por ejemplo, podemos simplificar ligeramente el código anterior:
Main.java
12345678910111213141516package com.example; import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("Unlock", "Infinity", "with", "Codefinity"); System.out.println("List of strings: " + strings); List<String> list = strings.stream() .filter(e -> e.length() > 5) .map(e -> e.substring(1, 4)) .toList(); System.out.println("Modified list: " + list); } }
Al encadenar múltiples métodos de flujo juntos, se recomienda colocar cada método en una nueva línea para mejorar significativamente la legibilidad del código.
El método flatMap() transforma cada elemento de un stream en un nuevo stream y combina los resultados en un solo stream. En otras palabras, con este método, podemos dividir el stream en varios streams, y luego se fusionarán en un solo stream. Por ejemplo, tienes una lista de cadenas donde cada cadena puede contener más de una palabra, como una lista de nombres y apellidos. Y necesitas poner en mayúscula la primera letra de cada una de estas palabras:
Main.java
123456789101112131415161718192021222324package com.example; import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<String> users = Arrays.asList("Ethan Johnson", "Olivia smith", "mason davis", "Ava taylor", "logan brown", "Emma Anderson", "jackson miller"); System.out.println("List of users: " + users); List<String> list = users.stream() .flatMap(e -> Arrays.stream(e.split(" "))) .map(e -> capitalizeFirstLetter(e)) .toList(); System.out.println("List with capitalized names and surnames: " + list); } private static String capitalizeFirstLetter(String word) { if (word == null || word.isEmpty()) { return word; } return Character.toUpperCase(word.charAt(0)) + word.substring(1); } }
En el código anterior, escribimos un método privado separado que pone en mayúscula la primera letra de una palabra y utilizamos este método en el método map() junto con una expresión lambda.
Observa que al usar el método flatMap, dividimos cada elemento del stream en diferentes streams utilizando el método Arrays.stream(e.split(" ")). Debido a que el método split() devuelve un array, necesitamos usar el método Arrays.stream() para dividir este array en streams.
Luego, todos estos streams se fusionan en un solo stream, después de lo cual usamos el método que escribimos. Ahora tenemos todos los nombres y apellidos de usuarios con la primera letra en mayúscula.
¿Sabes qué sería interesante?
Si colocamos estos nombres y apellidos en un HashMap, donde la clave es el apellido y el valor es el nombre.
Vamos a implementarlo en el código:
Main.java
12345678910111213141516171819202122232425262728293031323334package com.example; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; public class Main { public static void main(String[] args) { List<String> users = Arrays.asList("Ethan Johnson", "Olivia smith", "mason davis", "Ava taylor", "logan brown", "Emma Anderson", "jackson miller"); System.out.println("List of users: " + users); List<String> list = users.stream() .flatMap(e -> Arrays.stream(e.split(" "))) .map(e -> capitalizeFirstLetter(e)) .toList(); System.out.println("List with capitalized names and surnames: " + list); Map<String, String> usersKeyValue = new HashMap<>(); for (int i = 0; i < list.size() - 1; i+=2) { String name = list.get(i); String surname = list.get(i + 1); usersKeyValue.put(surname, name); } System.out.println("Map with surnames as keys and names as values: " + usersKeyValue); } private static String capitalizeFirstLetter(String word) { if (word == null || word.isEmpty()) { return word; } return Character.toUpperCase(word.charAt(0)) + word.substring(1); } }
Con un bucle simple, almacenamos el nombre y el apellido en variables y luego en el mapa. Observe cómo funciona el bucle. Incrementamos la variable i en 2 en cada iteración porque necesitamos saltar el apellido una vez que ya lo hemos registrado.
- El método
distinct()elimina duplicados del stream. En general, esto puede ser útil si se necesitan elementos únicos en el stream o si se desea eliminar rápidamente duplicados de una lista. Esto se puede lograr fácilmente con la siguiente construcción:
list.stream().distinct().toList()
-
El método
sortedordena todos los elementos del stream en orden natural, desde el número más pequeño hasta el más grande o en orden alfabético. Esto también puede ser útil si se necesita un stream ordenado o si se requiere ordenar rápidamente una lista; -
El método
skip(n)omite los primerosnelementos del stream. Esto puede ser útil al trabajar con archivos de texto, donde las primeras n líneas pueden ser, por ejemplo, metadatos o una descripción del archivo. También es relevante mencionar el métodolimit(n), que generalmente limita la cantidad de elementos en el stream. Incluso si se crea un stream con 1000 elementos y luego se utilizalimit(200), el stream contendrá solo los primeros 200 elementos.
Main.java
123456789101112package com.example; import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<Integer> example = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); example = example.stream().skip(3).limit(5).toList(); System.out.println("List: " + example); } }
Estos son los principales métodos intermedios que necesitarás utilizar. Puedes explorar el resto de los métodos consultando la enlace a la documentación oficial de Java. Continuemos con los métodos terminales.
Métodos terminales
-
El método terminal con el que ya estás familiarizado es
toList(). Convierte el stream en una lista y la retorna. En otras palabras, puedes asignar directamente este stream con métodos a una lista. Este método fue introducido en Java 17 y reemplaza la construcción más complejacollect(Collectors.toList()); -
El método
collect()también convierte el stream en una estructura de datos específica. Utiliza, como parámetro, un método de la interfazCollectors. Esta interfaz contiene métodos comotoList(),toSet()ytoCollection(). Por ejemplo:
Main.java
123456789101112131415package com.example; import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.stream.Collectors; public class Main { public static void main(String[] args) { List<Integer> example = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); Set<Integer> integerSet = example.stream().collect(Collectors.toSet()); System.out.println("List: " + example); System.out.println("Set: " + integerSet); } }
El método forEach() recibe una expresión lambda y realiza una acción específica para cada elemento en el flujo.
Por ejemplo:
Main.java
1234567891011package com.example; import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<Integer> example = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); example.stream().forEach(e -> System.out.println(e + 1)); } }
La diferencia entre este método y el método map es que este método es terminal, y después de él, no se pueden llamar otros métodos.
Estos son todos los métodos básicos para trabajar con streams. Es un tema complejo, y puede que no lo comprendas de inmediato. Sin embargo, es un tema que se domina con la práctica. En los próximos capítulos prácticos sobre streams, tendrás muchas oportunidades para trabajar con ellos, ya que es una forma muy conveniente y práctica de manipular listas y arreglos de datos.
1. ¿Cuál es el propósito principal de la Stream API en Java?
2. ¿Cuál de las siguientes es una operación terminal en la Stream API?
3. ¿Qué hace la operación map en la API de Stream?
4. ¿En qué se diferencia la operación flatMap de map en la API de Stream?
5. ¿Qué hace la operación filter en la API de Stream?
6. ¿Cuál es el propósito de la operación forEach en la Stream API?
7. ¿Cuál de las siguientes es una operación intermedia en la Stream API?
8. ¿Cómo se utiliza la operación limit en la Stream API?
¡Gracias por tus comentarios!