Introducción a los subprocesos de Java en Java 8
Java Streams, introducido como parte de Java 8, se utiliza para trabajar con colecciones de datos. No son una estructura de datos en sí mismos, pero se pueden utilizar para ingresar información de otras estructuras de datos ordenándola y canalizándola para producir un resultado final. Nota: Es importante no confundir Stream y Thread, ya que en ruso ambos términos a menudo se denominan en la misma traducción "flujo". Stream denota un objeto para realizar operaciones (con mayor frecuencia transferir o almacenar datos), mientras que Thread (traducción literal - hilo) denota un objeto que permite ejecutar cierto código de programa en paralelo con otras ramas de código. Debido a que Stream no es una estructura de datos separada, nunca cambia la fuente de datos. Las secuencias de Java tienen las siguientes características:-
Java Stream se puede utilizar utilizando el paquete “java.util.stream”. Se puede importar a un script usando el código:
import java.util.stream.* ;
Con este código, también podemos implementar fácilmente varias funciones integradas en Java Stream.
-
Java Stream puede aceptar entradas de colecciones de datos, como colecciones y matrices en Java.
-
Java Stream no requiere cambiar la estructura de datos de entrada.
-
Java Stream no cambia la fuente. En cambio, genera resultados utilizando métodos de canalización apropiados.
-
Los Java Streams están sujetos a operaciones intermedias y terminales, que analizaremos en las siguientes secciones.
-
En Java Stream, las operaciones intermedias se canalizan y ocurren en un formato de evaluación diferido. Terminan con funciones terminales. Este forma el formato básico para usar Java Stream.
Crear una secuencia de Java en Java 8
Los subprocesos de Java se pueden crear de varias formas:1. Crear una secuencia vacía usando el método Stream.empty()
Puede crear una secuencia vacía para usarla más adelante en su código. Si utiliza el método Stream.empty() , se generará una secuencia vacía que no contiene valores. Esta secuencia vacía puede resultar útil si queremos omitir una excepción de puntero nulo en tiempo de ejecución. Para hacer esto puedes usar el siguiente comando:Stream<String> str = Stream.empty();
La declaración anterior generará una secuencia vacía llamada str sin ningún elemento dentro de ella. Para verificar esto, simplemente verifique el número o tamaño de la transmisión usando el término str.count() . Por ejemplo,
System.out.println(str.count());
Como resultado, obtenemos 0 en la salida .
2. Cree una secuencia utilizando el método Stream.builder() con una instancia de Stream.Builder
También podemos usar Stream Builder para crear una secuencia usando el patrón de diseño del constructor. Está diseñado para la construcción de objetos paso a paso. Veamos cómo podemos crear una instancia de una transmisión usando Stream Builder .Stream.Builder<Integer> numBuilder = Stream.builder();
numBuilder.add(1).add(2).add( 3);
Stream<Integer> numStream = numBuilder.build();
Con este código, puede crear una secuencia llamada numStream que contenga elementos int . Todo se hace bastante rápido gracias a la instancia de Stream.Builder llamada numBuilder que se crea primero.
3. Cree una secuencia con los valores especificados usando el método Stream.of()
Otra forma de crear una secuencia implica utilizar el método Stream.of() . Esta es una forma sencilla de crear una secuencia con valores determinados. Declara y también inicializa el hilo. Un ejemplo del uso del método Stream.of() para crear una secuencia:Stream<Integer> numStream = Stream.of(1, 2, 3);
Este código creará una secuencia que contiene elementos int , tal como lo hicimos en el método anterior usando Stream.Builder . Aquí hemos creado directamente una secuencia usando Stream.of() con valores predefinidos [1, 2 y 3] .
4. Cree una secuencia a partir de una matriz existente utilizando el método Arrays.stream()
Otro método común para crear un hilo implica el uso de matrices en Java. La secuencia aquí se crea a partir de una matriz existente utilizando el método Arrays.stream() . Todos los elementos de la matriz se convierten en elementos de secuencia. He aquí un buen ejemplo:Integer[] arr = {1, 2, 3, 4, 5};
Stream<Integer> numStream = Arrays.stream(arr);
Este código generará un numStream que contiene el contenido de una matriz llamada arr, que es una matriz de números enteros.
5. Fusionar dos transmisiones existentes usando el método Stream.concat()
Otro método que se puede utilizar para crear una secuencia es el método Stream.concat() . Se utiliza para combinar dos hilos para crear un solo hilo. Ambas corrientes se combinan en orden. Esto significa que el primer hilo va primero, seguido del segundo, y así sucesivamente. Un ejemplo de dicha concatenación se ve así:Stream<Integer> numStream1 = Stream.of(1, 2, 3, 4, 5);
Stream<Integer> numStream2 = Stream.of(1, 2, 3);
Stream<Integer> combinedStream = Stream.concat( numStream1, numStream2);
La declaración anterior creará una secuencia final llamada CombinedStream que contiene elementos de la primera secuencia numStream1 y de la segunda secuencia numStream2 uno por uno .
Tipos de operaciones con Java Stream
Como ya se mencionó, puede realizar dos tipos de operaciones con Java Stream en Java 8: intermedias y terminales. Veamos cada uno de ellos con más detalle.Operaciones intermedias
Las operaciones intermedias generan un flujo de salida y se ejecutan solo cuando se encuentran con una operación de terminal. Esto significa que las operaciones intermedias se ejecutan de forma perezosa, canalizadas y solo pueden completarse mediante una operación de terminal. Aprenderá sobre la evaluación diferida y la canalización un poco más adelante. Ejemplos de operaciones intermedias son los siguientes métodos: filter() , map() , different() , peek() , sorted() y algunos otros.Operaciones terminales
Las operaciones de terminal completan la ejecución de operaciones intermedias y también devuelven los resultados finales del flujo de salida. Debido a que las operaciones de terminal señalan el final de la ejecución diferida y la canalización, este subproceso no se puede volver a utilizar después de haber pasado por una operación de terminal. Ejemplos de operaciones de terminal son los siguientes métodos: forEach() , Collect() , count() , reduce() y así sucesivamente.Ejemplos de operaciones con Java Stream
Operaciones intermedias
A continuación se muestran algunos ejemplos de algunas operaciones intermedias que se pueden aplicar a un flujo de Java:filtrar()
Este método se utiliza para filtrar elementos de una secuencia que coinciden con un predicado específico en Java. Estos elementos filtrados forman una nueva secuencia. Echemos un vistazo a un ejemplo para comprenderlo mejor.Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> even = numStream.filter(n -> n % 2 == 0) .collect(Collectors.toList()); System.out.println(even);
Conclusión:
mapa()
Este método se utiliza para crear una nueva secuencia ejecutando funciones asignadas en elementos de la secuencia de entrada original. Quizás la nueva secuencia tenga un tipo de datos diferente. El ejemplo se ve así:Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> d = numStream.map(n -> n*2) .collect(Collectors.toList()); System.out.println(d);
Conclusión:
distinto()
Este método se utiliza para recuperar solo elementos individuales en una secuencia filtrando duplicados. Un ejemplo de lo mismo se ve así:Stream<Integer> numStream = Stream.of(43,65,1,98,63,63,1); List<Integer> numList = numStream.distinct() .collect(Collectors.toList()); System.out.println(numList);
Conclusión:
ojeada()
Este método se utiliza para rastrear cambios intermedios antes de ejecutar una operación de terminal. Esto significa que peek() se puede utilizar para realizar una operación en cada elemento de una secuencia para crear una secuencia en la que se pueden realizar más operaciones intermedias.Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> nList = numStream.map(n -> n*10) .peek(n->System.out.println("Mapped: "+ n)) .collect(Collectors.toList()); System.out.println(nList);
Conclusión:
ordenado()
El método sorted() se utiliza para ordenar los elementos de una secuencia. De forma predeterminada, ordena los elementos en orden ascendente. También puede especificar un orden de clasificación específico como parámetro. Un ejemplo de implementación de este método se ve así:Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); numStream.sorted().forEach(n -> System.out.println(n));
Conclusión:
Operaciones terminales
A continuación se muestran algunos ejemplos de algunas operaciones de terminal que se pueden aplicar a flujos de Java:para cada()
El método forEach() se utiliza para recorrer todos los elementos de una secuencia y ejecutar la función en cada elemento uno por uno. Esto actúa como una alternativa a declaraciones de bucle como for , while y otras. Ejemplo:Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); numStream.forEach(n -> System.out.println(n));
Conclusión:
contar()
El método count() se utiliza para recuperar el número total de elementos presentes en la secuencia. Es similar al método size() , que se utiliza a menudo para determinar el número total de elementos de una colección. Un ejemplo del uso del método count() con Java Stream es el siguiente:Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); System.out.println(numStream.count());
Conclusión:
recolectar()
El método Collect() se utiliza para realizar reducciones mutables de elementos de flujo. Se puede utilizar para eliminar contenido de una transmisión una vez completado el procesamiento. Utiliza la clase Collector para realizar reducciones .Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> odd = numStream.filter(n -> n % 2 == 1) .collect(Collectors.toList()); System.out.println(odd);
Conclusión:
mín() y máx()
El método min() , como su nombre indica, se puede utilizar en una secuencia para encontrar el elemento mínimo en ella. De manera similar, el método max() se puede utilizar para encontrar el elemento máximo en una secuencia. Intentemos entender cómo se pueden utilizar con un ejemplo:Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); int smallest = numStream.min((m, n) -> Integer.compare(m, n)).get(); System.out.println("Smallest element: " + smallest);
numStream = Stream.of(43, 65, 1, 98, 63); int largest = numStream.max((m, n) -> Integer.compare(m, n)).get(); System.out.println("Largest element: " + largest);
Conclusión:
encontrarAny() y encontrarPrimero()
findAny() devuelve cualquier elemento de la secuencia como Opcional . Si la secuencia está vacía, también devolverá un valor Opcional , que estará vacío. findFirst() devuelve el primer elemento de la secuencia como Opcional . Al igual que con el método findAny() , el método findFirst() también devuelve un parámetro opcional vacío si la secuencia correspondiente está vacía. Echemos un vistazo al siguiente ejemplo basado en estos métodos:Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); Optional<Integer> opt = numStream.findFirst();System.out.println(opt); numStream = Stream.empty(); opt = numStream.findAny();System.out.println(opt);
Conclusión:
allMatch() , anyMatch() y noneMatch()
El método allMatch() se utiliza para comprobar si todos los elementos de una secuencia coinciden con un determinado predicado y devuelve el valor booleano verdadero si lo hacen; de lo contrario, devuelve falso . Si la secuencia está vacía, devuelve verdadero . El método anyMatch() se utiliza para comprobar si alguno de los elementos de una secuencia coincide con un determinado predicado. Devuelve verdadero si es así, falso en caso contrario. Si la secuencia está vacía, devuelve falso . El método noneMatch() devuelve verdadero si ningún elemento de la secuencia coincide con el predicado y falso en caso contrario. Un ejemplo para ilustrar esto se ve así:Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); boolean flag = numStream.allMatch(n -> n1); System.out.println(flag); numStream = Stream.of(43, 65, 1, 98, 63); flag = numStream.anyMatch(n -> n1); System.out.println(flag); numStream = Stream.of(43, 65, 1, 98, 63); flag = numStream.noneMatch(n -> n==1);System.out.println(flag);
Conclusión:
Evaluaciones diferidas en Java Stream
La evaluación diferida conduce a optimizaciones cuando se trabaja con Java Streams en Java 8. Implican principalmente retrasar las operaciones intermedias hasta que se encuentra una operación de terminal. La evaluación diferida es responsable de evitar el desperdicio innecesario de recursos en los cálculos hasta que el resultado sea realmente necesario. El flujo de salida resultante de las operaciones intermedias se genera sólo después de que se ha completado la operación terminal. La evaluación diferida funciona con todas las operaciones intermedias en flujos de Java. Un uso muy útil de la evaluación diferida se produce cuando se trabaja con flujos infinitos. De esta manera se evitan muchos procesamientos innecesarios.Tuberías en Java Stream
Una canalización en un Java Stream consta de un flujo de entrada, cero o más operaciones intermedias alineadas una tras otra y, finalmente, una operación de terminal. Las operaciones intermedias en Java Streams se realizan de forma perezosa. Esto hace inevitables las operaciones intermedias canalizadas. Con las canalizaciones, que son básicamente operaciones intermedias combinadas en orden, se hace posible la ejecución diferida. Las canalizaciones ayudan a realizar un seguimiento de las operaciones intermedias que deben realizarse después de que finalmente se encuentre una operación terminal.Conclusión
Resumamos ahora lo que hemos aprendido hoy. En este articulo:- Echamos un vistazo rápido a qué son Java Streams.
- Luego aprendimos muchas técnicas diferentes para crear subprocesos Java en Java 8.
- Aprendimos dos tipos principales de operaciones (operaciones intermedias y operaciones de terminal) que se pueden realizar en flujos de Java.
- Luego analizamos en detalle varios ejemplos de operaciones intermedias y terminales.
- Terminamos aprendiendo más sobre la evaluación diferida y finalmente aprendiendo sobre la canalización en subprocesos de Java.
GO TO FULL VERSION