Este documento presenta el uso de expresiones lambda en Java para filtrar listas de manzanas según criterios como color y peso. Inicialmente se muestran métodos para filtrar por cada criterio de forma independiente, lo que resulta en código repetitivo. Luego se introducen predicados y expresiones lambda para parametrizar el comportamiento de filtrado de una manera más genérica y concisa. Finalmente, se explican conceptos como inferencia de tipos, alcance y métodos de referencia para referirse a métodos de manera más compacta.
3. Expresiones Lambda
Funciones como valores/parámetros
public static List<Apple> filterGreenApples(List<Apple> inventory) {
List<Apple> result = new ArrayList<>();
for(Apple apple: inventory){
if( "green".equals(apple.getColor() ) {
result.add(apple);
}
}
return result;
}
Solo manzanas verdes
Crear un método que retorne un listado que contenga solo las manzanas
de color verde del listado pasado por parámetro
Código tomado del libro Java 8 in Action
4. Expresiones Lambda
Funciones como valores/parámetros
public static List<Apple> filterApplesByColor(List<Apple> inventory, String color) {
List<Apple> result = new ArrayList<>();
for(Apple apple: inventory){
if( apple.getColor().equals(color) ) {
result.add(apple);
}
}
return result;
}
Solo manzanas del color
deseado
Pero con el tiempo, también se requiere poder filtrar las manzanas de
color rojo, por lo que intuitivamente adicionamos un parámetro más...
Código tomado del libro Java 8 in Action
5. Expresiones Lambda
Funciones como valores/parámetros
public static List<Apple> filterApplesByWeight(List<Apple> inventory, int weight) {
List<Apple> result = new ArrayList<>();
for(Apple apple: inventory){
if( apple.getWeight() > weight ) {
result.add(apple);
}
}
return result;
}
Solo manzanas con un peso
mínimo
Pero también deseamos separar las manzanas dado su peso, lo que nos
lleva a adicionar el siguiente método...
Código tomado del libro Java 8 in Action
6. Expresiones Lambda
Funciones como valores/parámetros
…
List<Apple> inventory = ...
//Separar manzanas por color
List<Apple> greenApples = filterApplesByColor(inventory, "green");
List<Apple> redApples = filterApplesByColor(inventory, "red");
//Separar manzanas por peso
List<Apple> heavyGreenApples = filterApplesByWeight(greenApples, 150);
List<Apple> heavyRedApples = filterApplesByWeight(redApples, 150);
...
Con los anteriores métodos, podríamos escribir código que nos permita
realizar el filtrado de nuestras manzanas de la siguiente manera...
Código tomado del libro Java 8 in Action
7. Expresiones Lambda
Funciones como valores/parámetros
public static List<Apple> filterApples(List<Apple> inventory, String color, int weight,
boolean flag) {
List<Apple> result = new ArrayList<>();
for (Apple apple: inventory){
if ( (flag && apple.getColor().equals(color)) ||
(!flag && apple.getWeight() > weight) ){
result.add(apple);
}
}
return result;
}
Solo manzanas que cumplan
con el color o el peso
Sin embargo, para no caer en DRY (Don't Repeat Yourself) se podría
tener un sólo método que haga el filtro dependiendo de una bandera...
Código tomado del libro Java 8 in Action
8. Expresiones Lambda
Funciones como valores/parámetros
…
List<Apple> inventory = ...
//Separar manzanas por color o peso
List<Apple> greenApples = filterApples(inventory, "green", 0, true);
List<Apple> heavyApples = filterApples(inventory, "", 150, false);
...
Con el anterior método, podríamos escribir código que nos permita
realizar el filtrado de nuestras manzanas de la siguiente manera...
Código tomado del libro Java 8 in Action
9. Expresiones Lambda
Funciones como valores/parámetros
Predicado: Función que
retorna un resultado booleano
Podemos crear una capa de
abstracción para modelar los
criterios de búsqueda.
Pros
Método filtrar sería genérico
Cons
Eventualmente muchas clases
Patrón de diseño Estrategia
<<interface>>
ApplePredicate
+test(Apple):boolean
AppleGreenColorPredicate AppleRedColorPredicate
AppleHeavyWeightPredicateAppleRedAndHeavyPredicate
+test(Apple):boolean +test(Apple):boolean
+test(Apple):boolean +test(Apple):boolean
10. Expresiones Lambda
Funciones como valores/parámetros
public class AppleHeavyWeightPredicate implements ApplePredicate{
public boolean test(Apple apple){
return apple.getWeight() > 150;
}
}
public class AppleGreenColorPredicate implements ApplePredicate{
public boolean test(Apple apple){
return "green".equals(apple.getColor());
}
}
Con este nivel de abstracción, tendríamos que crear una clase por cada
criterio que deseemos...
Código tomado del libro Java 8 in Action
11. Expresiones Lambda
Funciones como valores/parámetros
public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate p){
List<Apple> result = new ArrayList<>();
for(Apple apple: inventory){
if(p.test(apple)){
result.add(apple);
}
}
return result;
}
Con este nivel de abstracción, solo tendríamos que preocuparnos de que
la condición de nuestro predicado se cumpla...
Código tomado del libro Java 8 in Action
Nuestro predicado encapsula
la condición
12. Expresiones Lambda
Funciones como valores/parámetros
…
List<Apple> inventory = ...
//Separar manzanas por color
List<Apple> greenApples = filterApples(inventory, new
AppleGreenColorPredicate());
//Separar manzanas por peso
List<Apple> heavyApples = filterApples(inventory, new
AppleHeavyWeightPredicate());
...
Con el anterior método y nuestro nivel de abstracción, podríamos escribir
código que nos permita realizar el filtrado de nuestras manzanas de la
siguiente manera...
Código tomado del libro Java 8 in Action
public boolean test(Apple apple){
return apple.getWeight() > 150;
}
13. Expresiones Lambda
Funciones como valores/parámetros
List<Apple> redApples = filterApples(inventory, new ApplePredicate() {
public boolean test(Apple apple){
return "red".equals(apple.getColor());
}
});
List<Apple> greenApples = filterApples(inventory,
new ApplePredicate() {
public boolean test(Apple apple){
return "green".equals(apple.getColor());
}
});
Sin embargo, tendríamos que tener muchas clases para nuestros
predicados y eso va en contra del principio DRY. Java ofrece un
mecanismo con el cuál podemos declarar e instanciar clases al tiempo...
Código tomado del libro Java 8 in Action
Enredado, código repetido, etc
14. Expresiones Lambda
Funciones como valores/parámetros
List<Apple> inventory = …
List<Apple> redApples = filterApples(inventory,
(Apple a) -> "red".equals(apple.getColor());
List<Apple> greenApples = filterApples(inventory,
(Apple a) -> "green".equals(apple.getColor()));
En cambio, con expresiones Lambda, podemos pasar funciones como
valores/parámetros y nuestro código lucirá mucho más limpio y conciso
Código tomado del libro Java 8 in Action
Parametrización de comportamiento
15. Expresiones Lambda
Definición
Métodos anónimos, es decir, métodos sin nombre o clase.
Paquete java.util.function
Permite escribir código más claro que usar clases anónimas.
(parámetros) -> {cuerpo expresión lambda}
Operador
lambda
16. Expresiones Lambda
Definición
Ejemplos de expresiones lambda:
(int a, int b) -> a + b
(int a) -> a + 1
(int a, int b) -> { System.out.println(a + b); return a + b; }
• El cuerpo puede lanzar excepciones
• Una sola línea no requiere llaves ni la sentencia return
• Varias líneas requieren separador punto y coma (;)
• Un solo parámetro no requiere paréntesis
• Ningún parámetro requiere paréntesis vacíos
Parámetros Cuerpo
Operador
lambda
17. Expresiones Lambda
Definiciones – Interfaces Recargadas - Métodos por Defecto
Llamados Métodos por Defecto, Métodos
Virtuales o Métodos Defensores
Especificados e implementados en la
Interface.
La implementación por defecto es
usada solo cuando la clase
implementadora no provee su propia
implementación
Desde el punto de vista de quién invoca al
método, es un método más de la
Interface.
Especial cuidado cuando se implementen
Interfaces con métodos por defecto
iguales…
public interface MorningInterface {
default void saludo(){
System.out.println("Buenos días");
}
}
public interface AfternoonInterface {
default void saludo(){
System.out.println("Buenas tardes");
}
}
Interface List<T> {
…
default void sort(Comparator<? super T> cmp)
{
Collections.sort(this, cmp);
}
…
}
18. Expresiones Lambda
Definiciones – Interfaces Recargadas - Métodos por Defecto
Con la adición de métodos por defecto, las clases ahora pueden heredar diferentes
comportamientos de múltiples interfaces.
En caso de conflictos, este es el orden en el que se selecciona el método por defecto:
1. Implementaciones en clases concretas
2. Implementaciones en subinterfaces
3. Explícitamente seleccionando el método usando: X.super.m(...)
Donde X es la interface y m es el método deseado
También debemos notar que, a partir de Java 8, además de métodos por defecto, las interfaces
también pueden proveer implementación de métodos estáticos.
19. Expresiones Lambda
Definiciones – Interfaces Recargadas - Métodos por Defecto
public class MultipleInheritance implements MorningInterface, AfternoonInterface {
public static void main(String... args) {
MultipleInheritance m = new MultipleInheritance();
m.saludo(); //Saludo desde clase implementadora
m.saludoMañanero(); //Buenos días
m.saludoTarde(); //Buenas tardes
}
@Override
public void saludo() { System.out.println("Saludo desde clase implementadora"); }
public void saludoMañanero() { MorningInterface.super.saludo(); }
public void saludoTarde() { AfternoonInterface.super.saludo(); }
}
En este caso es obligatorio implementar el métodoEn este caso es obligatorio implementar el método
20. Expresiones Lambda
Definiciones – Interfaces Funcionales
SAM: Single Abstract Method.
Interface Funcional: Interface con
un solo método abstracto (SAM) y
diferente a los métodos de Object
(toString, equals…).
@FunctionalInterface
Nueva anotación. Indica al
compilador que debe verificar si se
trata de una interfase funcional.
@FunctionalInterface
public interface InterfaceEjemplo{
/** Imprime algo*/
void imprimir();
/** Método de Object, no cuenta contra SAM*/
boolean equals(Object obj);
}
//Ejemplo de Interfases funcionales que ya conocemos:
Interface Runnable { void run(); }
Interface Comparator<T> {
boolean compare(T x, T y);
}
Interface ActionListener { void actionPerformed(…); }
21. Expresiones Lambda
Definiciones – Interface Funcional – 4 Grandes Grupos
@FunctionalInterface
public interface Supplier<T> {
T get();
//Otros métodos...
}
@FunctionalInterface
public interface Function<T, R> {
R apply(T);
//Otros métodos...
}
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
//Otros métodos...
}
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
//Otros métodos...
}
Uso: Mapear de un valor a otro
Uso: Creación de objetosUso: Validación de criterios
Uso: Consumir métodos del parámetro.
Tiene efectos secundarios
22. Expresiones Lambda
Definiciones – Inferencia de tipos
//Comparator usando clases anónimas...
Comparator<String> c = new Comparator<String>() {
public int compare(String x, String y) {
return x.length() - y.length();
}
};
//Comparator usando Lambdas...
Comparator<String> c = (x, y) -> x.length() - y.length();
Contexto de asignaciónContexto de asignación
23. Expresiones Lambda
Definiciones – Inferencia de tipos
//Comparator usando clases anónimas...
Comparator<String> c = new Comparator<String>() {
public int compare(String x, String y) {
return x.length() - y.length();
}
};
//Comparator usando Lambdas...
Comparator<String> c = (x, y) -> x.length() - y.length();
Contexto de asignaciónContexto de asignación
24. Expresiones Lambda
Definiciones – Casos especiales
//Misma expresión lambda, diferentes interfaces funcionales
Callable<Integer> c = () -> 42;
PrivilegedAction<Integer> p = () -> 42;
//void-compatibility rule
List<String> list = ...
Predicate<String> p = s -> list.add(s);
Consumer<String> c = s -> list.add(s);
Aunque el método abstracto de Consumer
retorna void, no hay error de compilación
25. Expresiones Lambda
Definiciones – Alcance
Las expresiones Lambda pueden usar
variables o parámetros definidos como
constantes (final) o que sean efectivamente
constantes.
Efectivamente Constante: Variable o
parámetro que solo es asignado una vez,
incluso si no se ha definido usando la
palabra final.
Como si la expresión lambda usara el valor
más no la variable...
A diferencia de las clases anónimas, en
Lambdas la palabra this hace referencia a la
instancia de la clase sobre la cual se ha
escrito la expresión Lambda.
void expire(File root, long before) {
...
root.listFiles(File p -> p.lastModified() <= before);
...
//Esto causaría error de compilación
//before = 10000;
}
class SessionManager {
long before = ...;
void expire(File root) {
...
root.listFiles(File p -> checkExpiry(p.lastModified(),
this.before));
}
boolean checkExpiry(long time, long expiry) { ... }
}
26. Expresiones Lambda
Definiciones – Métodos de Referencia
Métodos de Referencia: Invocación de
métodos por su nombre. Expresiones
Lambda más compactas y fáciles de leer.
Solo para cuando la expresión lambda
tiene una sola sentencia.
Existen 4 tipos:
Métodos Estáticos
Métodos de Instancia de un objeto
Métodos de Instancia de algún tipo
Constructores
//Uso para métodos estáticos
Clase::metodoEstatico
//Uso para métodos de instancia de un objeto existente
Objeto::metodoDeInstancia
//Uso para métodos de instancia de algún tipo de objeto
Tipo::metodoDeInstancia
//Uso para constructores
Clase::new
public class Person {
String name;
LocalDate birthday;
//gets…sets…
public static int compareByAge(Person a, Person b) {
return a.birthday.compareTo(b.birthday);
}
}
27. Expresiones Lambda
Definiciones – Métodos de Referencia::Métodos Estáticos
public class Testing
{
public static void main(String… args)
{
Person[] personArray = new Person[1000];
…
//ingresar objetos tipo persona al arreglo...
…
//Ordenar el arreglo usando lambdas
Arrays.sort(personArray, (a, b) -> Person.compareByAge(a, b));
//Ordenar el arreglo usando métodos de referencia
Arrays.sort(personArray, Person::compareByAge);
}
}
28. Expresiones Lambda
Definiciones – Métodos de Referencia::Métodos de Instancia de un objeto
public class Testing
{
public static void main(String… args)
{
Person[] personArray = new Person[1000];
//ingresar objetos tipo persona al arreglo...
ComparisonProvider myComparisonProvider = new ComparisonProvider();
//Ordenar el arreglo usando lambdas
Arrays.sort(personArray, (a,b)->myComparisonProvider.compareByName(a, b));
//Ordenar el arreglo usando métodos de referencia
Arrays.sort(personArray, myComparisonProvider::compareByName);
}
}
class ComparisonProvider {
public int compareByName(Person a, Person b) { return a.getName().compareTo(b.getName());}
}
29. Expresiones Lambda
Definiciones – Métodos de Referencia::Métodos de Instancia de algún Tipo
public class Testing
{
public static void main(String… args)
{
String[] stringArray = {"Zuñiga", "James", "Cuadrado", "John",
"Armero", "Murillo", "Falcao", "Martínez"};
//Ordenar el arreglo usando lambdas
Arrays.sort(stringArray, (a, b) -> a.compareToIgnoreCase(b));
//Ordenar el arreglo usando métodos de referencia
Arrays.sort(stringArray, String::compareToIgnoreCase);
}
}
30. Expresiones Lambda
Definiciones – Métodos de Referencia::Constructores
public class Testing
{
public static void main(String… args)
{
//Crear una persona usando lambdas
Supplier<Persona> s1 = () -> new Persona();
Persona p1 = s1.get();
//Crear una persona usando métodos de referencia
Supplier<Persona> s1 = Persona::new;
Persona p1 = s1.get();
}
}
31. Expresiones Lambda
Definiciones – Métodos de Referencia::Constructores
public class Manzana{
private int peso;
public Manzana(int w){ peso = w;}
}
…
//Crear una manzana usando lambdas
Function<Integer, Manzana> f1 = (w) -> new Manzana(w);
Manzana m1 = f1.apply(100);
//Crear una manzana usando métodos de referencia
Function<Integer, Manzana> f1 = Manzana::new;
Manzana m1 = f1.apply(100);
...
32. Expresiones Lambda
Definiciones – Expresiones Compuestas
Muchas de las interfaces funcionales que
ofrece Java 8 cuentan con métodos
denominados “Métodos por defecto”.
Permiten la composición de expresiones
para obtener expresiones más complejas.
Predicate<T>: Incluye métodos and, or,
negate.
Function<T,R>: Incluye métodos andThen
y compose.
Comparator<T>: Incluye métodos
reversed, comparing, thenComparing.
//Creación de predicados complejos
Predicate<String> p1 = s -> s.length() > 3;
Predicate<String> p2 = s -> s.charAt(0) == 'A';
Predicate<String> p3 = ...
//Precedencia de izquierda a derecha
Predicate<String> p4 = p1.or(p2).and(p3).negate();
//Composición de funciones
Function<String, Integer> f1 = String::length;
Function<Integer, Integer> f2 = i -> i * 2;
//Primero aplica f1 y al resultado aplica f2
Function<String, Integer> f3 = f1.andThen(f2);
//Composición de comparadores
Comparator<String> c1 = (a,b) -> a.length() - b.length();
Comparator<String> c2 = String::compareTo;
//Primero compare con c1 y si son iguales use c2
Comparator<String> c3 = c1.thenComparing(c2);
33. Stream API
Definiciones - Stream
Stream: Secuencia de elementos de una fuente que soporta
operaciones para el procesamiento de sus datos:
1. De forma declarativa usando expresiones lambda.
2. Es posible el encadenamiento de varias operaciones haciendo al
código fácil de leer y con un objetivo claro.
3. Operaciones se ejecutan de forma secuencial o paralela (Fork/Join)
Paquete java.util.stream
Interfase java.util.stream.Stream
Map
Reduce
Filter
34. Stream API
Definiciones - Stream
Un Stream está compuesto de tres (3) partes:
1. Una fuente de información
2. Cero o más operaciones intermedias
3. Una operación final: Produce un resultado o un efecto en los datos
List transacciones = ...
int sum = transacciones.stream().
filter(t -> t.getProveedor().getCiudad().equals(“Cali”)).
mapToInt(Transaccion::getPrecio).
sum();
Fuente
Operaciones intermedias
Operación terminal
35. Stream API
Definiciones - Stream
Propiedades de un Stream:
1. Operaciones intermedias retornan otro Stream = Encadenamiento de operaciones
2. Operaciones intermedias son encoladas hasta que una operación terminal sea invocada
3. Solo puede ser recorrido una vez (IllegalStateException)
4. Iteración interna = Iteración automática
●
Iteración secuencial o paralela transparante para el desarrollador
●
Permite definir el “Qué quiero lograr” en vez del “Cómo lo quiero lograr”
9. Versiones “primitivas” evitan el Autoboxing y Unboxing
●
DoubleStream
●
IntStream
●
LongStream
36. Stream API
Definiciones – Stream - Creación
Diferentes formas de obtener un Stream
1. Stream.of(T): Stream<T> -> Retorna un Stream ordenado y secuencial de los elementos pasados
por parámetro
2. Stream.empty():Stream -> Retorna un Stream secuencial vacío.
3. Arrays.stream(T[]):Stream<T> -> Retorna un Stream secuencial del arreglo pasado por parámetro.
Versión “primitiva” retorna: DoubleSTream, IntStream, LongStream.
4. Collection<E>.stream():Stream<E> -> Retorna un Stream secuencial de los elementos de la
colección. Versión en paralelo: Collection<E>.parallelStream():Stream<E>
5. Stream.iterate(T, UnaryOperator<T>):Stream<T> -> Retorna un Stream infinito, ordenado y
secuencial, a partir del valor inicial T y de aplicar la función UnaryOperator al valor inicial para
obtener los demás elementos. Para limitar su tamaño, se puede usar el método +limit(long):Stream
6. Stream.generate(Supplier<T>):Stream<T> -> Retorna un Stream infinito, scuencial pero no
ordenado, a partir de una expresión lambda que provee los elementos.
37. Stream API
Operaciones sobre colecciones de datos - Filter
Para filtrar los elementos de un Stream podemos usar los siguientes métodos:
+filter(Predicate<T>):Stream<T> -> Retorna un Stream que contiene solo los
elementos que cumplen con el predicado pasado por parámetro.
+distinct():Stream<T> -> Retorna un Stream sin elementos duplicados.
Depende de la implementación de +equals(Object):boolean.
+limit(long):Stream<T> -> Retorna un Stream cuyo tamaño no es mayor al
número pasado por parámetro. Los elementos son cortados hasta ese
tamaño.
+skip(long):Stream<T> -> Retorna un Stream que descarta los primeros N
elementos, donde N es el número pasado por parámetro. Si el Stream
contiene menos elementos que N, entonces retorna un Stream vacío.
Map
Reduce
Filter
38. Stream API
Operaciones sobre colecciones de datos - Map
Podemos transformar los elementos de un Stream al extraer información de
éstos. Para lograrlo podemos usar alguno de los siguientes métodos:
+map(Function<T, R>): Stream<R> -> Retorna un Stream que contiene el
resultado de aplicar la función pasada por parámetro a todos los elementos
del Stream. Transforma los elementos de T a R.
También existe en su versión “primitiva”:
+mapToDouble(ToDoubleFunction<T>): DoubleStream
+mapToInt(ToIntFunction<T>): IntStream
+mapToLong(ToLongFunction<T>): LongStream
La ventaja de usar las versiones primitivas radica en que se evita el uso de
Autoboxing y Unboxing, lo que en algunas situaciones puede ser deseado por
temas de rendimiento.
Map
Reduce
Filter
39. Stream API
Operaciones sobre colecciones de datos - Map
+flatMap(Function<T, Stream<R>):Stream<R>
Permite transformar cada elemento en un Stream y al final concatenarlos en
un solo Stream.
También existe en su versión “primitiva”:
+flatMapToDouble(Function<T, DoubleStream): DoubleStream
+flatMapToInt(Function<T, IntStream): IntStream
+flatMapToLong(Function<T, LongStream): LongStream
La ventaja de usar las versiones primitivas radica en que se evita el uso de
Autoboxing y Unboxing, lo que en algunas situaciones puede ser deseado por
temas de rendimiento.
Ejemplo
Veamos el siguiente ejemplo, dado un arreglo de cadenas de texto, contar la
cantidad de palabras diferentes.
Map
Reduce
Filter
40. Stream API
Operaciones sobre colecciones de datos - Map
Taller Lambdas y Stream APITaller
Lambdas y Stream APITallerTaller
Lambdas y Stream APITallerTaller
2
Stream<String>
map(s->s.split(“ “))
Stream<String[]>
distinct()
Stream<String[]>
count()
ERROR!
41. Stream API
Operaciones sobre colecciones de datos - Map
Taller Lambdas y Stream APITaller
Lambdas y Stream APITallerTaller
5
Stream<String>
map(s->s.split(“ “))
Stream<String[]>
distinct()
Stream<Stream<String>>
count()
flatMap(Arrays::stream)
Lambdas y Stream APITallerTaller
Lambdas y Stream APITaller
Arreglo de String
CORRECTO!
Stream<String>
42. Stream API
Operaciones sobre colecciones de datos - Reduce
Por último, las operaciones terminales provocan que todas las operaciones
intermedias sean ejecutadas.
Existen operaciones terminales que permiten obtener datos del Stream
como: conteo, mínimo, máximo, búsqueda, y en general reducir el Stream
a un valor.
Existen algunas operaciones terminales cuyo propósito es el el consumo
de los elementos del Stream, por ejemplo: +foreach(Consumer<T>):void
Existen otras operaciones terminales que permiten recolectar los
elementos de un Stream en estructuras mutables.
Map
Reduce
Filter
43. Stream API
Operaciones sobre colecciones de datos – Reduce - Agregación
Entre las operaciones terminales que permiten obtener datos del Stream
tenemos:
+count():long -> Retorna la cantidad de elementos en el Stream
+max(Comparator<T>):Optional<T> -> Retorna el elemento máximo del
Stream basado en el comparador pasado por parámetro. Nótese que el
retorno es de tipo Optional.
+min(Comparator<T>):Optional<T> -> Retorna el elemento mínimo del
Stream basado en el comparador pasado por parámetro. Nótese que el
retorno es de tipo Optional.
Map
Reduce
Filter
44. Stream API
Operaciones sobre colecciones de datos – Reduce - Búsqueda
+allMatch(Predicate<T>):boolean -> Verifica si todos los elementos del
Stream satisfacen el predicado pasado por parámetro. Si alguno no lo
cumple, para la verificación y retorna falso.
+anyMatch(Predicate<T>):boolean -> Verifica si alguno de los elementos
del Stream satisface el predicado pasado por parámetro. Si alguno lo
cumple, para la verificación y retorna verdadero.
+noneMatch(Predicate<T>):boolean -> Verifica si todos los elementos del
Stream NO satisfacen el predicado pasado por parámetro. Si alguno SI lo
cumple, para la verificación y retorna false.
+findAny():Optional<T> -> Retorna algún elemento del Stream. Nótese el
retorno es de tipo Optional.
+findFirst():Optional<T>: Retorna el primer elemento del Stream. Nótese
el retorno es de tipo Optional.
Map
Reduce
Filter
45. Stream API
Operaciones sobre colecciones de datos – Reduce
En general, si queremos reducir un Stream a
un valor, podemos hacer uso del siguiente
método:
+reduce(BinaryOperator<T>):Optional<T>
Realiza la reducción del Stream usando una
función asociativa. Nótese el retorno es de
tipo Optional.
+reduce(T, BinaryOperator<T>):T
Realiza la reducción del Stream usando un
valor inicial y una función asociativa.
Existe una versión aún más genérica del
método reduce que se sale del alcance de
esta presentación.
List<Integer> numeros = …
//Obtiene el posible número mayor par del Stream
Optional<Integer> opt = numeros
.stream()
.filter(x -> x % 2 == 0)
.reduce(Integer::max);
//Obtiene la suma de los elementos del Stream
Integer suma = numeros
.stream()
.reduce(0, (x,y) -> x + y);
46. Stream API
Operaciones sobre colecciones de datos – Reduce - Collectors
collect es una operación terminal que permite
recolectar los elementos de un Stream.
Collectors es una clase utilitaria que provee
métodos estáticos que retornan los
recolectores más usados. Dichos recolectores
pueden ser agrupados en 3 tipos:
●
Reducción y resúmen: Reducen el Stream
y permite obtener valores agregados.
●
Agrupamiento: Agrupa elementos en un
Map usando una función de clasificación.
●
Particionamiento: Agrupamiento donde la
función de clasificación es un predicado.
Agrupa los elementos en un Map de 2
llaves: false y true
Import static java.util.stream.Collectors.*;
...
List<Integer> numeros = …
//Reducción y resúmen: Cuenta
long cuenta = numeros.stream().collect(counting());
//Reducción y resúmen: Suma
int suma = numeros
.stream()
.collect(summingInt(x -> x.intValue()));
//Reducción y resúmen: Objeto resúmen
IntSummaryStatistics res = numeros
.stream()
.collect(summarizingInt(Integer::intValue));
//Reducción y resúmen: Unión de cadenas
String csv = numeros.stream().map(Object::toString)
.collect(joining(", "));
summarizingLong
summarizingDouble
summingLong
summingDouble
47. Stream API
Operaciones sobre colecciones de datos – Reduce - Collectors
Import static java.util.stream.Collectors.*;
…
List<Empleado> empleados = …
// Agrupamiento
Map<String, List<Empleado>> porDept = empleados
.stream()
.collect(groupingBy(Empleado::getDepartmento));
Map<String, Long> deptCant = empleados
.stream()
.collect(groupingBy(Empleado::getDepartmento), counting());
Map<String, Map<String, List<Empleado>>> dptoCiu = null;
dptoCiu = empleados
.stream()
.collect(groupingBy(Empleado::getDepartamento, groupingBy(Empleado::getCiudad)));
Función de clasificación
En el Map quedan solo las llaves
que tengan valores
Segundo collector permite
agrupar n-niveles
Segundo collector permite
Contar la cantidad de elementos
48. Stream API
Operaciones sobre colecciones de datos – Reduce - Collectors
Import static java.util.stream.Collectors.*;
…
List<Estudiante> estudiantes = …
//Estudiantes separados entre los que ganaron y perdieron el curso
Map<Boolean, List<Estudiante>> valoracion = estudiantes
.stream()
.collect(partitioningBy(s -> s.getNota() >= 3.0));
//Cantidad de estudiantes separados entre los que ganaron y perdieron el curso
Map<Boolean, Long> valoracion = estudiantes
.stream()
.collect(partitioningBy(s -> s.getNota() >= 3.0, counting()));
Función de clasificación
Segundo collector permite
contar la cantidad de elementos
particionar n-niveles, etc.
49. Stream API
Debug
+peek(Consumer<T>):Stream<T>
●
Operación Intermedia
●
Cada elemento es pasado al Consumer
●
No modificar los elementos del Stream
Cuando se requiera definir breakpoints
●
Usar métodos de referencia
●
Usar peek entre operaciones intermedias
List<String> palabras = ...
List<String> unicas = palabras.stream()
.flatMap(w -> Stream.of(w.split(“ “)))
.peek(s -> s)
.map(String::lowerCase)
.distinct()
.collect(Collectors.toList());
50. Stream API
Streams Paralelos
Propiedades de un Stream Paralelo:
1. Usa Fork/Join internamente
2. Usar +parallel():Stream para convertir a paralelo.
3. Usar +sequential():Stream para convertir a secuencial.
4. La última llamada a +parallel():Stream o +sequential():Stream es la que se tiene en cuenta.
5. ¿Cuántos hilos en paralelo? Runtime.getRuntime.availableProcessors()
6. Propiedad: "java.util.concurrent.ForkJoinPool.common.parallelism" modifica valor por defecto
7. El uso de Streams en Paralelo requiere más trabajo, no siempre terminará más rápido que
Streams secuenciales.
51. Stream API
Streams Paralelos - ¿Cuándo usarlos?
Consideraciones a tener en cuenta si se desea usar Streams en Paralelo:
1. Medir primero
●
N = Número de items
●
Q = costo de procesar un item
●
N*Q = Costo total de las operaciones. Mientras más grande, mejor usar Streams Paralelos
2. Evitar el Autoboxing y Unboxing
3. Algunas operaciones no se comportan bien en paralelo
●
+limit(long):Stream<T>
●
+findFirst():Optional<T>
●
+sorted():Stream<T>
●
+distinct():Stream<T>
4. Usar estructuras que sean fáciles de descomponer
●
ArrayList -> Exelente
●
HashSet, TreeSet -> Bien
●
LinkedList -> Mal
52. Enlaces de interés
Java SE 8 API docs
http://download.java.net/jdk8/docs/api/
JDK 8 Download
http://www.oracle.com/technetwork/java/javase/overview/
Grupo de usuarios Java en Cali
http://www.clojug.org/
Virtual Java User Group
http://virtualjug.com/
Java 8 in Action
http://www.manning.com/urma/