package ar.com.javacuriosities.streams;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.OptionalDouble;
import java.util.stream.Collectors;
/*
* Nos referimos a Terminal Operation a aquellas que generan un resultado o un side-effect,
* recién al ejecutar este tipo de operaciones se consume el pipeline, esto permite
* - Lazy evaluation
* - Merged/Fused operations
* - Elimination of redundant operations
* - Parallel execution
*/
public class Lesson05TerminalOperation {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(33);
numbers.add(66);
numbers.add(11);
numbers.add(33);
numbers.add(44);
numbers.add(77);
numbers.add(99);
numbers.add(88);
numbers.add(22);
// Tenemos varios métodos para hacer matching de elementos
// findFirst(): Nos retorna el primer elemento por medio de un Optional en el caso que el Stream este vacio
Optional<Integer> first = numbers.stream().filter(n -> n > 66).findFirst();
first.ifPresent(n -> System.out.println("First: " + n));
// findAny(): Funciona igual que findFirst() pero sirve para parallel streams
Optional<Integer> findAny = numbers.stream().findAny();
findAny.ifPresent(n -> System.out.println("Find Any: " + n));
// allMatch(): Indica si todos los elementos del stream matchean el predicado
boolean allMatch = numbers.stream().allMatch(n -> n > 66);
System.out.println("All Match: " + allMatch);
// anyMatch(): Indica si algún elemento del stream matchea el predicado
boolean anyMatch = numbers.stream().anyMatch(n -> n > 66);
System.out.println("Any Match: " + anyMatch);
// noneMatch(): Indica si todos los elementos del stream NO matchean el predicado
boolean noneMatch = numbers.stream().noneMatch(n -> n > 66);
System.out.println("None Match: " + noneMatch);
// collect(): Nos permite hacer una reducción mutable del Stream
Integer collect = numbers.stream().collect(Collectors.summingInt(n -> n));
System.out.println("Collect: " + collect);
// toArray(): Nos permite obtener un array desde un Stream
Object[] array = numbers.stream().toArray();
System.out.println("Array: " + Arrays.toString(array));
// count(): Retorna la cantidad de elementos en el Stream
long count = numbers.stream().count();
System.out.println("Count: " + count);
// max(): Recibe un comparator y obtiene el máximo valor del Stream usando ese Comparator
Optional<Integer> max = numbers.stream().max((n1, n2) -> n1.compareTo(n2));
max.ifPresent(n -> System.out.println("Max: " + max));
// min(): Recibe un comparator y obtiene el mínimo valor del Stream usando ese Comparator
Optional<Integer> min = numbers.stream().min((n1, n2) -> n1.compareTo(n2));
min.ifPresent(n -> System.out.println("Min: " + min));
// forEach(): Nos permite recorrer el Stream
System.out.println("For Each");
numbers.parallelStream().forEach(System.out::println);
// forEachOrdered() Nos garantiza el orden (Si es que existiera), esto es respetado incluso para los parallel Stream
System.out.println("For Each Ordered");
numbers.parallelStream().forEachOrdered(System.out::println);
// reduce(): Nos permite hacer una reducción del Stream, o sea recorre el Stream y con un lambda BinaryOperator recibimos un accumulator y el valor del elemento actual
Optional<Integer> reduce = numbers.stream().reduce((accumulator, value) -> accumulator + value);
reduce.ifPresent(n -> System.out.println("Reduce: " + reduce));
/*
* Tenemos algunos métodos específicos para Stream primitivos, por ejemplo
* sum()
* average()
*/
OptionalDouble average = numbers.stream().mapToInt(n -> n).average();
average.ifPresent(n -> System.out.println("Average: " + n));
int sum = numbers.stream().mapToInt(n -> n).sum();
System.out.println("Sum: " + sum);
}
}