====== Streams ======

===== ¿Qué son los Streams? =====

Los streams son clases que encapsulan colecciones o arrays y permiten que éstos soporten operaciones utilizando lambdas. De esa forma se
simplifica operar sobre los elementos de toda la colección o sobre la propia colección en si.

===== Cómo crear Streams =====

Los ''streams'' se pueden generar llamando al método ''stream()''  de cualquiera de las implementaciones de la interfaz ''java.util.Collection''.

<code java>
List<Animal> listaAnimales = new ArrrayList<>();
listaAnimales.add(. . .);
listaAnimales.add(. . .);
listaAnimales.add(. . .);
Stream<Animal> streamAnimales = listaAnimales.stream();
</code>

También se pueden crear a partir de objetos de forma individual, sin partir de ninguna colección ya existente:

<code java>
Stream<String> streamNombres = Stream.of("Juan", "Francisco", "Ana", "María");
</code>

Y también se puede crear un stream a partir de un array:

<code java>
String[] nombres = {"Juan", "Francisco", "Ana", "María"};
Stream<String> streamNombres = Stream.of(nombres);
</code>

===== Operaciones intermedias =====

Son aquellas operaciones que siempre acaban devolviendo el stream modificado.

  * ''distinct()'': Devuelve un stream con los elementos distintos
  * ''filter(Predicate<? super T> predicado)'': Devuelve un stream con los elementos que cumplen el filtro pasado como parámetro
  * ''limit(long tamanoMaximo)'': Devuelve un stream con tantos elementos como el valor pasado como parámetro
  * ''map(Function<? super T>, ? extends R> mapeador)'': Devuelve un stream cuyo elementos será el resultado de la operación realizada sobre los elementos del stream original
  * ''peek(Consumer<? super T> accion)'': 
  * ''skip(long n)'': Elimina los \emph{n} primeros elementos y devuelve un stream con los restantes
  * ''sorted(Comparator<? super T> comparador)'': Devuelve un stream ordenado según el comparador que se pasa como parámetro
  * ''unordered()'': Devuelve un stream desordenado

===== Operaciones finales =====

En este caso, son las operaciones que devuelven como resultado algo diferente que el propio stream.

  * ''boolean allMatch(Predicate<? super T> predicado)'': Comprueba si todos los elementos del stream cumplen el predicado
  * ''boolean anyMatch(Predicate<? super T> predicado)'': Comprueba si algún elemento del stream cumple el predicado
  * ''boolean noneMatch(Predicate<? super T> predicado)'': Comprueba que ningún elemento del stream cumpla el predicado
  * ''Optional<T> findAny()'': Devuelve un elemento cualquier del stream
  * ''Optional<T> findFirst()'': Devuelve el primer elemento del stream
  * ''R collect(Collector<? super T,A,R> colector)'': Devuelve el stream como un tipo colección
  * ''long count()'': Devuelve el número de elementos del stream
  * ''void forEach(Consumer<? super T> accion)'': Permite ejecutar una operación sobre cada uno de los elementos del stream
  * ''void forEachOrdered(Consumer<? super T> accion)'': Permite ejecutar una operación sobre cada uno de los elementos del
    stream ordenado
  * ''Optional<T> max(Comparator<? super T> comparador)'': Devuelve el mayor de los elementos, según el comparador que se pasa
    como parámetro
  * ''Optional<T> min(Comparator<? super T> comparador)'': Devuelve el menor de los elementos, según el comparador que se pasa
    como parámetro
  * ''Object[] toArray()'': Devuelve el stream como array de Objetos
  * ''Iterator<T> iterator()'': Devuelve el stream como un Iterator

Veamos algunos ejemplos para la siguiente colección:

<code java>
List<String> numeros = new ArrayList<>();
numeros.add("uno");
numeros.add("dos");
numeros.add("cinco");
numeros.add("tres");
numeros.add("cuatro");
numeros.add("cinco");
</code>

<code java>
numeros.stream()
    .filter(nombre -> nombre.startsWith("c"))
    .forEach(System.out::println);
</code>

<code java>
cinco
cuatro
cinco
</code>

<code java>
numeros.stream()
    .distinct()
    .filter(nombre -> nombre.startsWith("c"))
    .forEach(System.out::println);
</code>

<code java>
cuatro
cinco
</code>

<code java>
numeros.stream()
    .filter(nombre -> nombre.startsWith("c"))
    .peek(nombre -> System.out.println(nombre.length()))
    .forEach(System.out::println);
</code>

<code>
5
cinco
6
cuatro
5
cinco
</code>

<code java>
numeros.stream()
    .peek(nombre -> System.out.println(nombre.length()))
    .filter(nombre -> nombre.startsWith("c"))
    .forEachOrdered(System.out::println);
</code>

<code java>
3
3
5
cinco
4
6
cuatro
5
cinco
</code>

<code java>
Optional<String> primerNumero = numeros.stream().findAny();
System.out.println(primerNumero.get());
</code>

<code java>
uno
</code>

<code java>
Optional<String> primerNumero = numeros.stream().findFirst();
System.out.println(primerNumero.get());
</code>

<code java>
uno
</code>

<code java>
// Orden no garantizado
numeros.stream().parallel().forEach(System.out::println);
</code>

<code java>
tres
cinco
cuatro
dos
cinco
uno
</code>

<code java>
// Orden garantizado
numeros.stream().parallel().forEachOrdered(System.out::println);
</code>

<code java>
uno
dos
cinco
tres
cuatro
cinco
</code>

<code java>
Optional<String> primerNumero = numeros.stream().parallel().findAny();
</code>

<code java>
tres
</code>

<code java>
List<String> numeros = numeros.stream()
                            .sorted(Comparator.comparing(nombre::length)
                            .collect(Collectors.toList());
</code>

----

(c) 2021-2023 Santiago Faci