Skip to content

Commit 214c2e3

Browse files
committed
class 25 27 28 Streams theory
1 parent e1fb8d5 commit 214c2e3

3 files changed

Lines changed: 789 additions & 0 deletions

File tree

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
package com.platzi.functional_student_practice._15_streams;
2+
3+
import com.platzi.functional_teacher_theory.util.Utils;
4+
5+
import java.util.List;
6+
import java.util.function.Predicate;
7+
import java.util.stream.Collectors;
8+
import java.util.stream.Stream;
9+
10+
public class C25CollectorsTheory {
11+
12+
/*
13+
14+
Operaciones y Collectors
15+
16+
Usando Stream nos podemos simplificar algunas operaciones, como es el filtrado, el mapeo,
17+
conversiones y más. Sin embargo, no es del tod0 claro cuándo una operación nos devuelve otro
18+
Stream para trabajar y cuándo nos da un resultado final…
19+
20+
¡O al menos no era claro hasta ahora!
21+
22+
Cuando hablamos de pasar lambdas a una operación de Stream, en realidad, estamos delegando a
23+
Java la creación de un objecto basado en una interfaz.
24+
25+
Por ejemplo:
26+
27+
*/
28+
29+
public static void main(String[] args) {
30+
31+
Stream<String> coursesStream = Utils.getListOf("Java", "Node.js", "Kotlin").stream();
32+
List<String> javaCoursesStream = coursesStream.filter(course -> course.contains("Java")).toList();
33+
34+
// En realidad, es lo mismo que:
35+
List<String> explicitOperationStream = coursesStream.filter(new Predicate<String>() {
36+
public boolean test(String st) {
37+
return st.contains("Java");
38+
}
39+
}).toList();
40+
}
41+
42+
/*
43+
44+
Estas interfaces las mencionamos en clases anteriores. Solo como repaso, listo algunas a
45+
continuación:
46+
47+
Consumer<T>: recibe un dato de tipo T y no genera ningún resultado
48+
Function<T,R>: toma un dato de tipo T y genera un resultado de tipo R
49+
Predicate<T>: toma un dato de tipo T y evalúa si el dato cumple una condición
50+
Supplier<T>: no recibe ningún dato, pero genera un dato de tipo T cada vez que es invocado
51+
UnaryOperator<T> recibe un dato de tipo T y genera un resultado de tipo T
52+
53+
Estas interfaces (y otras más) sirven como la base de donde generar los objetos con las lambdas
54+
que pasamos a los diferentes métodos de Stream. Cada una de ellas cumple esencialmente con
55+
recibir el tipo de dato de el Stream y generar el tipo de retorno que el método espera.
56+
57+
Si tuvieras tu propia implementación de Stream, se vería similar al siguiente ejemplo:
58+
59+
public class PlatziStream<T> implements Stream {
60+
private List<T> data;
61+
62+
public Stream<T> filter(Predicate<T> predicate) {
63+
List<T> filteredData = new LinkedList<>();
64+
for(T t : data){
65+
if(predicate.test(t)){
66+
filteredData.add(t);
67+
}
68+
}
69+
70+
return filteredData.stream();
71+
}
72+
}
73+
74+
Probablemente, tendría otros métodos y estructuras de datos, pero la parte que importa es justamente
75+
cómo se usa el Predicate. Lo que hace Stream internamente es pasar cada dato por este objeto que
76+
nosotros proveemos como una lambda y, según el resultado de la operación, decidir si debe incluirse
77+
o no en el Stream resultante.
78+
79+
Como puedes notar, esto no tiene mucha complejidad, puesto que es algo que pudimos fácilmente replicar.
80+
Pero Stream no solo incluye estas operaciones “triviales”, también incluye un montón de utilidades
81+
para que la máquina virtual de Java pueda operar los elementos de un Stream de manera más rápida y
82+
distribuida.
83+
84+
85+
86+
Operaciones
87+
88+
A estas funciones que reciben lambdas y se encargan de trabajar (operar) sobre los datos de un Stream
89+
generalmente se les conoce como Operaciones.
90+
91+
Existen dos tipos de operaciones: intermedias y finales.
92+
93+
Cada operación aplicada a un Stream hace que el Stream original ya no sea usable para más operaciones.
94+
Es importante recordar esto, pues tratar de agregar operaciones a un Stream que ya esta siendo
95+
procesado es un error muy común.
96+
97+
En este punto seguramente te parezcan familiares todas estas operaciones, pues vienen en forma de
98+
métodos de la interfaz Stream. Y es cierto. Aunque son métodos, se les considera operaciones,
99+
puesto que su intención es operar el Stream y, posterior a su trabajo, el Stream no puede volver a
100+
ser operado.
101+
102+
En clases posteriores hablaremos más a detalle sobre cómo identificar una operación terminal de una
103+
operación intermedia.
104+
105+
106+
107+
Collectors
108+
109+
Una vez que has agregado operaciones a tu Stream de datos, lo más usual es que llegues a un punto
110+
donde ya no puedas trabajar con un Stream y necesites enviar tus datos en otro formato, por ejemplo,
111+
JSON o una List a base de datos.
112+
113+
Existe una interfaz única que combina todas las interfaces antes mencionadas y que tiene como única
114+
utilidad proveer de una operación para obtener todos los elementos de un Stream: Collector.
115+
116+
Collector<T, A, R> es una interfaz que tomará datos de tipo T del Stream, un tipo de dato mutable A,
117+
donde se iran agregando los elementos (mutable implica que podemos cambiar su contenido, como un
118+
LinkedList), y generara un resultado de tipo R.
119+
120+
Suena complicado… y lo es. Por eso mismo, Java 8 incluye una serie de Collectors ya definidos para
121+
no rompernos las cabeza con cómo convertir nuestros datos.
122+
123+
Veamos un ejemplo:
124+
125+
*/
126+
127+
public List<String> getJavaCourses(Stream<String> coursesStream) {
128+
List<String> javaCourses = coursesStream
129+
.filter(course -> course.contains("Java"))
130+
.collect(Collectors.toList());
131+
return javaCourses;
132+
}
133+
134+
/*
135+
136+
Usando java.util.stream.Collectors podemos convertir muy sencillamente un Stream en un Set, Map,
137+
List, Collection, etc. La clase Collectors ya cuenta con métodos para generar un Collector que
138+
corresponda con el tipo de dato que tu Stream está usando. Incluso vale la pena resaltar que
139+
Collectors puede generar un ConcurrentMap que puede ser de utilidad si requieres de multiples
140+
threads.
141+
142+
Usar Collectors.toXXX es el proceso inverso de usar Collection.stream(). Esto hace que sea fácil
143+
generar APIs publicas que trabajen con estructuras/colecciones comunes e internamente utilizar
144+
Stream para agilizar las operaciones de nuestro lado.
145+
146+
147+
148+
Tipos de retorno
149+
150+
Hasta este punto, la única manera de obtener un dato que ya no sea un Stream es usando Collectors,
151+
pues la mayoría de operaciones de Stream se enfocan en operar los datos del Stream y generar un
152+
nuevo Stream con los resultados de la operación.
153+
154+
Sin embargo, algunas operaciones no cuentan con un retorno. Por ejemplo, forEach, que es una
155+
operación que no genera ningún dato. Para poder entender qué hace cada operación basta con
156+
plantear qué hace la operación para poder entender qué puede o no retornar.
157+
158+
Por ejemplo:
159+
160+
La operación de findAny trata de encontrar cualquier elemento que cumpla con la condición
161+
del Predicate que le pasamos como parámetro. Sin embargo, la operación dice que se devuelve
162+
un Optional. ¿Qué pasa cuando no encuentra ningún elemento? ¡Claro, por eso devuelve un
163+
Optional! Porque podría haber casos en que ningún elemento del Stream cumpla la condición.
164+
165+
En las clases posteriores haremos un listado más a detalle y con explicaciones de qué tipos de
166+
retorno tiene cada operación. Y entenderemos por qué se categorizan como operaciones finales e
167+
intermedias.
168+
169+
170+
Conclusiones
171+
172+
Por ahora, hemos entendido que cada operación en un Stream consume hasta agotar el Stream.
173+
Y lo hace en un objeto no reusable. Esto implica que tenemos que decidir en nuestro código
174+
cuándo un Stream es un elemento temporal para una función o cuándo realmente una función
175+
sera la última en tocar los datos del Stream.
176+
177+
Las siguientes clases y lecturas cubrirán mas a detalle las múltiples operaciones y cómo
178+
afectan a los datos del Stream.
179+
180+
*/
181+
}

0 commit comments

Comments
 (0)