Principios
Los principios de trabajo con la Stream API se basan en conceptos clave de la programación funcional y la evaluación perezosa. A continuación, se presentan los principales principios de la Stream API.
Evaluación Perezosa
Uno de los principios fundamentales de la Stream API es la evaluación perezosa.
Esto significa que las operaciones intermedias como filter() o map() no se ejecutan de inmediato. Simplemente forman una cadena de acciones que se ejecutarán únicamente cuando se invoque una operación terminal, como collect() o forEach().
Main.java
12345678910111213package com.example; import java.util.List; public class Main { public static void main(String[] args) { List<String> names = List.of("Alice", "Bob", "Charlie"); long count = names.stream() .filter(name -> name.startsWith("A")) // Forming the operation .count(); // Triggering execution System.out.println(count); // Output: 1 } }
Este código crea un stream a partir de una lista de cadenas, filtra los elementos para conservar solo aquellos que comienzan con la letra A, y cuenta cuántos cumplen la condición. Se imprime el resultado (1), ya que solo Alice satisface la condición.
Estilo de Programación Funcional
La Stream API utiliza expresiones lambda e interfaces funcionales para el procesamiento de datos. En lugar del enfoque imperativo con bucles, se describe lo que se debe hacer con los datos:
Main.java
1234567891011121314package com.example; import java.util.List; public class Main { public static void main(String[] args) { List<String> fruits = List.of("apple", "banana", "cherry", "apricot", "blueberry"); List<String> result = fruits.stream() .map(String::toUpperCase) // Convert all strings to uppercase .skip(3) .toList(); // Collect the result into a new list System.out.println(result); } }
Este código crea un stream a partir de una lista de frutas, convierte las cadenas a mayúsculas usando map(), y omite los primeros tres elementos con skip(). El resultado se recopila en una nueva lista y se imprime, comenzando desde el cuarto elemento.
Inmutabilidad de los datos
La Stream API no modifica los datos originales. Todas las operaciones crean un nuevo stream o devuelven un resultado sin modificar la colección o el arreglo. Esto mejora la seguridad de los datos y previene efectos secundarios inesperados.
Main.java
12345678910111213package com.example; import java.util.List; public class Main { public static void main(String[] args) { List<String> names = List.of("Alice", "Bob", "Charlie"); List<String> updateNames = names.stream() .filter(name -> name.startsWith("A")) .toList(); System.out.println(names); // Output: [Alice, Bob, Charlie] } }
La Stream API filtra la lista names para crear un nuevo stream, updateNames, que contiene solo los elementos que comienzan con la letra A. Sin embargo, la lista original names permanece sin cambios, ya que la Stream API no modifica los datos directamente, garantizando la seguridad de los datos y previniendo efectos secundarios.
Los Streams Son Consumibles Una Sola Vez
Un stream puede ser utilizado solo una vez. Después de realizar una operación terminal, el stream queda indisponible para un procesamiento posterior.
Main.java
123456789101112package com.example; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { Stream<String> stream = List.of("Alice", "Bob").stream(); stream.forEach(System.out::println); stream.forEach(System.out::println); // Error: Stream has already been used } }
Aquí, se crea un stream a partir de una lista de cadenas y se imprime cada elemento en la consola. Después de que el stream se utiliza por primera vez, no puede ser reutilizado, lo que provoca un error si se intenta llamar a forEach() nuevamente.
Procesamiento paralelo
La Stream API admite streams paralelos, lo que permite un procesamiento de datos más rápido en sistemas multinúcleo. Esto se explora en mayor detalle aquí
Main.java
1234567891011package com.example; import java.util.List; public class Main { public static void main(String[] args) { List<Integer> numbers = List.of(1, 2, 3, 4, 5); numbers.parallelStream() .forEach(System.out::println); // Elements are processed in parallel } }
Este código crea un stream paralelo a partir de una lista de números y muestra cada elemento en la consola. El uso de parallelStream() permite el procesamiento paralelo de los elementos de la lista, lo que puede acelerar la ejecución al trabajar con conjuntos de datos grandes.
Limpieza y legibilidad del código
El uso de la Stream API hace que el código sea más declarativo. En lugar de describir cómo realizar una tarea (como usar bucles), se describe qué se necesita hacer exactamente. Esto mejora la legibilidad y simplifica el mantenimiento del código.
Ejemplo con un bucle:
List<String> names = List.of("Alice", "Bob", "Charlie");
for (String name : names) {
System.out.println(name);
}
Ejemplo usando Stream API:
List<String> names = List.of("Alice", "Bob", "Charlie");
names.stream().forEach(System.out::println);
En el primer ejemplo de bucle, se describe explícitamente cómo iterar a través de la lista e imprimir los elementos, mientras que en el segundo ejemplo con Stream API, simplemente se especifica qué se debe hacer: iterar por los elementos e imprimirlos. Esto hace que el código sea más declarativo y legible.
¡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 lazy evaluation in the Stream API?
What are some common terminal and intermediate operations in streams?
How does the Stream API improve code readability compared to traditional loops?
Awesome!
Completion rate improved to 2.33
Principios
Desliza para mostrar el menú
Los principios de trabajo con la Stream API se basan en conceptos clave de la programación funcional y la evaluación perezosa. A continuación, se presentan los principales principios de la Stream API.
Evaluación Perezosa
Uno de los principios fundamentales de la Stream API es la evaluación perezosa.
Esto significa que las operaciones intermedias como filter() o map() no se ejecutan de inmediato. Simplemente forman una cadena de acciones que se ejecutarán únicamente cuando se invoque una operación terminal, como collect() o forEach().
Main.java
12345678910111213package com.example; import java.util.List; public class Main { public static void main(String[] args) { List<String> names = List.of("Alice", "Bob", "Charlie"); long count = names.stream() .filter(name -> name.startsWith("A")) // Forming the operation .count(); // Triggering execution System.out.println(count); // Output: 1 } }
Este código crea un stream a partir de una lista de cadenas, filtra los elementos para conservar solo aquellos que comienzan con la letra A, y cuenta cuántos cumplen la condición. Se imprime el resultado (1), ya que solo Alice satisface la condición.
Estilo de Programación Funcional
La Stream API utiliza expresiones lambda e interfaces funcionales para el procesamiento de datos. En lugar del enfoque imperativo con bucles, se describe lo que se debe hacer con los datos:
Main.java
1234567891011121314package com.example; import java.util.List; public class Main { public static void main(String[] args) { List<String> fruits = List.of("apple", "banana", "cherry", "apricot", "blueberry"); List<String> result = fruits.stream() .map(String::toUpperCase) // Convert all strings to uppercase .skip(3) .toList(); // Collect the result into a new list System.out.println(result); } }
Este código crea un stream a partir de una lista de frutas, convierte las cadenas a mayúsculas usando map(), y omite los primeros tres elementos con skip(). El resultado se recopila en una nueva lista y se imprime, comenzando desde el cuarto elemento.
Inmutabilidad de los datos
La Stream API no modifica los datos originales. Todas las operaciones crean un nuevo stream o devuelven un resultado sin modificar la colección o el arreglo. Esto mejora la seguridad de los datos y previene efectos secundarios inesperados.
Main.java
12345678910111213package com.example; import java.util.List; public class Main { public static void main(String[] args) { List<String> names = List.of("Alice", "Bob", "Charlie"); List<String> updateNames = names.stream() .filter(name -> name.startsWith("A")) .toList(); System.out.println(names); // Output: [Alice, Bob, Charlie] } }
La Stream API filtra la lista names para crear un nuevo stream, updateNames, que contiene solo los elementos que comienzan con la letra A. Sin embargo, la lista original names permanece sin cambios, ya que la Stream API no modifica los datos directamente, garantizando la seguridad de los datos y previniendo efectos secundarios.
Los Streams Son Consumibles Una Sola Vez
Un stream puede ser utilizado solo una vez. Después de realizar una operación terminal, el stream queda indisponible para un procesamiento posterior.
Main.java
123456789101112package com.example; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { Stream<String> stream = List.of("Alice", "Bob").stream(); stream.forEach(System.out::println); stream.forEach(System.out::println); // Error: Stream has already been used } }
Aquí, se crea un stream a partir de una lista de cadenas y se imprime cada elemento en la consola. Después de que el stream se utiliza por primera vez, no puede ser reutilizado, lo que provoca un error si se intenta llamar a forEach() nuevamente.
Procesamiento paralelo
La Stream API admite streams paralelos, lo que permite un procesamiento de datos más rápido en sistemas multinúcleo. Esto se explora en mayor detalle aquí
Main.java
1234567891011package com.example; import java.util.List; public class Main { public static void main(String[] args) { List<Integer> numbers = List.of(1, 2, 3, 4, 5); numbers.parallelStream() .forEach(System.out::println); // Elements are processed in parallel } }
Este código crea un stream paralelo a partir de una lista de números y muestra cada elemento en la consola. El uso de parallelStream() permite el procesamiento paralelo de los elementos de la lista, lo que puede acelerar la ejecución al trabajar con conjuntos de datos grandes.
Limpieza y legibilidad del código
El uso de la Stream API hace que el código sea más declarativo. En lugar de describir cómo realizar una tarea (como usar bucles), se describe qué se necesita hacer exactamente. Esto mejora la legibilidad y simplifica el mantenimiento del código.
Ejemplo con un bucle:
List<String> names = List.of("Alice", "Bob", "Charlie");
for (String name : names) {
System.out.println(name);
}
Ejemplo usando Stream API:
List<String> names = List.of("Alice", "Bob", "Charlie");
names.stream().forEach(System.out::println);
En el primer ejemplo de bucle, se describe explícitamente cómo iterar a través de la lista e imprimir los elementos, mientras que en el segundo ejemplo con Stream API, simplemente se especifica qué se debe hacer: iterar por los elementos e imprimirlos. Esto hace que el código sea más declarativo y legible.
¡Gracias por tus comentarios!