package ar.com.javacuriosities.streams;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
/*
* Los Streams pueden ser ejecutados de forma paralela o secuencial eso puede depender de la fuente o
* de si nosotros cambiamos su procesamiento, esto puede ser cambiado muchas veces pero solo la ultima
* definición es la que se aplica.
*
* La implementación de Parallel Stream usa el Framework Fork/Join, por defecto este usa un numero de Threads
* igual a la cantidad de procesadores informados por el sistema operativo. Nosotros podemos cambiar esto
* por medio de la siguiente system property
*
* System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "32767");
*
* Es importante notar y recordar que no siempre obtendremos mejor performance por medio de Streams paralelos,
* esto puede varias por distintos motivos, al final siempre debemos hacer profiling para confirmar nuestra decision
*
* Para definir si usar parallel stream debemos tener en cuenta multiples aspectos:
*
* - Estructura de datos:
* ArrayList, HashSet, TreeSet -> Puede ser una buena opción
* LinkedList -> No tan aconsejado
*
* - Tipo de operación:
*
* filter() y map() -> Son excelentes candidatos para operaciones paralelas
* sorted() y distinct() -> No funcionan bien para operaciones paralelas
*
* - Tamaño y costo:
*
* N = Numero de elementos
* Q = Costo de la operación para cada elemento
* N x Q = Costo total
* Mientras mas grande es N x Q es mejor para hacer esas operaciones de forma paralela
*/
public class Lesson10ParallelStreams {
public static void main(String[] args) {
List<String> programmingLanguages = new ArrayList<>();
programmingLanguages.add("Scala");
programmingLanguages.add("Java");
programmingLanguages.add("PHP");
programmingLanguages.add("JavaScript");
// En base al Stream source podemos crear Stream secuenciales o paralelos
Stream<String> sequentialStream = programmingLanguages.stream();
Stream<String> parallelStream = programmingLanguages.parallelStream();
// Si concatenamos un Stream secuencial con uno paralelo el resultado es un Stream paralelo
Stream.concat(sequentialStream, parallelStream);
// El Stream usara el ultimo modo asignado ya este sea parallel o sequential, debemos indicar la salvedad que el método findFirst siempre devolverá lo mismo sin importar el modo
Stream<String> streamOfNames = Stream.of("Cosme Fulanito", "Pablo Marmol", "Pedro Picapiedra", "Super Sonico");
Optional<String> firstNameUsingFindFirst = streamOfNames.sequential().parallel().findFirst();
streamOfNames = Stream.of("Cosme Fulanito", "Pablo Marmol", "Pedro Picapiedra", "Super Sonico");
Optional<String> firstNameUsingFindAny = streamOfNames.sequential().parallel().findAny();
System.out.println("First Name Using Find First: " + firstNameUsingFindFirst);
System.out.println("First Name Using Find Any: " + firstNameUsingFindAny);
}
}