Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Aprende API de Stream en Java | Características y Técnicas Avanzadas de Java
Quizzes & Challenges
Quizzes
Challenges
/
Estructuras de Datos en Java

bookAPI 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() y parallelStream() 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

Main.java

copy
12
List<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

Example.java

copy
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

Example.java

copy
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

Main.java

copy
1234567891011121314
package 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

Main.java

copy
12345678910111213
package 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

Main.java

copy
1234567891011121314
package 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

Main.java

copy
123456789101112131415
package 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

Main.java

copy
12345678910111213141516
package 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

Main.java

copy
12345678910111213141516
package 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

Main.java

copy
123456789101112131415161718192021222324
package 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

Main.java

copy
12345678910111213141516171819202122232425262728293031323334
package 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 sorted ordena 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 primeros n elementos 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étodo limit(n), que generalmente limita la cantidad de elementos en el stream. Incluso si se crea un stream con 1000 elementos y luego se utiliza limit(200), el stream contendrá solo los primeros 200 elementos.

Main.java

Main.java

copy
123456789101112
package 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 compleja collect(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 interfaz Collectors. Esta interfaz contiene métodos como toList(), toSet() y toCollection(). Por ejemplo:

Main.java

Main.java

copy
123456789101112131415
package 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

Main.java

copy
1234567891011
package 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?

question mark

¿Cuál es el propósito principal de la Stream API en Java?

Select the correct answer

question mark

¿Cuál de las siguientes es una operación terminal en la Stream API?

Select the correct answer

question mark

¿Qué hace la operación map en la API de Stream?

Select the correct answer

question mark

¿En qué se diferencia la operación flatMap de map en la API de Stream?

Select the correct answer

question mark

¿Qué hace la operación filter en la API de Stream?

Select the correct answer

question mark

¿Cuál es el propósito de la operación forEach en la Stream API?

Select the correct answer

question mark

¿Cuál de las siguientes es una operación intermedia en la Stream API?

Select the correct answer

question mark

¿Cómo se utiliza la operación limit en la Stream API?

Select the correct answer

¿Todo estuvo claro?

¿Cómo podemos mejorarlo?

¡Gracias por tus comentarios!

Sección 4. Capítulo 3

Pregunte a AI

expand

Pregunte a AI

ChatGPT

Pregunte lo que quiera o pruebe una de las preguntas sugeridas para comenzar nuestra charla

Suggested prompts:

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?

bookAPI 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() y parallelStream() 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

Main.java

copy
12
List<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

Example.java

copy
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

Example.java

copy
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

Main.java

copy
1234567891011121314
package 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

Main.java

copy
12345678910111213
package 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

Main.java

copy
1234567891011121314
package 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

Main.java

copy
123456789101112131415
package 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

Main.java

copy
12345678910111213141516
package 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

Main.java

copy
12345678910111213141516
package 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

Main.java

copy
123456789101112131415161718192021222324
package 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

Main.java

copy
12345678910111213141516171819202122232425262728293031323334
package 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 sorted ordena 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 primeros n elementos 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étodo limit(n), que generalmente limita la cantidad de elementos en el stream. Incluso si se crea un stream con 1000 elementos y luego se utiliza limit(200), el stream contendrá solo los primeros 200 elementos.

Main.java

Main.java

copy
123456789101112
package 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 compleja collect(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 interfaz Collectors. Esta interfaz contiene métodos como toList(), toSet() y toCollection(). Por ejemplo:

Main.java

Main.java

copy
123456789101112131415
package 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

Main.java

copy
1234567891011
package 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?

question mark

¿Cuál es el propósito principal de la Stream API en Java?

Select the correct answer

question mark

¿Cuál de las siguientes es una operación terminal en la Stream API?

Select the correct answer

question mark

¿Qué hace la operación map en la API de Stream?

Select the correct answer

question mark

¿En qué se diferencia la operación flatMap de map en la API de Stream?

Select the correct answer

question mark

¿Qué hace la operación filter en la API de Stream?

Select the correct answer

question mark

¿Cuál es el propósito de la operación forEach en la Stream API?

Select the correct answer

question mark

¿Cuál de las siguientes es una operación intermedia en la Stream API?

Select the correct answer

question mark

¿Cómo se utiliza la operación limit en la Stream API?

Select the correct answer

¿Todo estuvo claro?

¿Cómo podemos mejorarlo?

¡Gracias por tus comentarios!

Sección 4. Capítulo 3
some-alt