SlideShare una empresa de Scribd logo
1 de 53
Alexis López
@aa_lopez
Agenda
Expresiones Lambda
Stream API
11
22
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
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
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
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
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
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
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
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
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
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;
}
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
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
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
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
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);
}
…
}
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.
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
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(…); }
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
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
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
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
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) { ... }
}
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);
}
}
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);
}
}
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());}
}
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);
}
}
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();
}
}
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);
...
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);
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
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
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
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.
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
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
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
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!
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>
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
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
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
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);
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
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
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.
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());
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.
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
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/
Lambdas y API Stream - Apuntes de Java

Más contenido relacionado

La actualidad más candente

Estilos de programación y sus lenguajes
Estilos de programación y sus lenguajesEstilos de programación y sus lenguajes
Estilos de programación y sus lenguajesPedro Contreras Flores
 
Practicas prolog
Practicas prologPracticas prolog
Practicas prologmaxsp5566
 
Clases y objetos de java
Clases y objetos de javaClases y objetos de java
Clases y objetos de javainnovalabcun
 
Java 8 introducción a expresiones lambdas y api stream
Java 8  introducción a expresiones lambdas y api streamJava 8  introducción a expresiones lambdas y api stream
Java 8 introducción a expresiones lambdas y api streamEudris Cabrera
 
Algoritmo de enrutamiento por inundación
Algoritmo de enrutamiento por inundaciónAlgoritmo de enrutamiento por inundación
Algoritmo de enrutamiento por inundaciónCarlos Martinez
 
Regular Expressions in Java
Regular Expressions in JavaRegular Expressions in Java
Regular Expressions in JavaOblivionWalker
 
Fundamentos de Programación
Fundamentos de ProgramaciónFundamentos de Programación
Fundamentos de ProgramaciónKudos S.A.S
 
Java interfaces & abstract classes
Java interfaces & abstract classesJava interfaces & abstract classes
Java interfaces & abstract classesShreyans Pathak
 
Programación Orientada a Objetos - Unidad 2: clases y objetos
Programación Orientada a Objetos - Unidad 2: clases y objetosProgramación Orientada a Objetos - Unidad 2: clases y objetos
Programación Orientada a Objetos - Unidad 2: clases y objetosJosé Antonio Sandoval Acosta
 
Lambda Expressions in Java
Lambda Expressions in JavaLambda Expressions in Java
Lambda Expressions in JavaErhan Bagdemir
 
Calculo relacional diapositivas
Calculo relacional diapositivasCalculo relacional diapositivas
Calculo relacional diapositivaslelyydrogo
 

La actualidad más candente (20)

Javascript
JavascriptJavascript
Javascript
 
Pointers in c++
Pointers in c++Pointers in c++
Pointers in c++
 
Type casting
Type castingType casting
Type casting
 
Estilos de programación y sus lenguajes
Estilos de programación y sus lenguajesEstilos de programación y sus lenguajes
Estilos de programación y sus lenguajes
 
Variables en Visual Basic 6.0
Variables en Visual Basic 6.0Variables en Visual Basic 6.0
Variables en Visual Basic 6.0
 
History of java'
History of java'History of java'
History of java'
 
Oop
OopOop
Oop
 
Traductor y su estructura
Traductor y su estructuraTraductor y su estructura
Traductor y su estructura
 
Practicas prolog
Practicas prologPracticas prolog
Practicas prolog
 
Clases y objetos de java
Clases y objetos de javaClases y objetos de java
Clases y objetos de java
 
Java 8 introducción a expresiones lambdas y api stream
Java 8  introducción a expresiones lambdas y api streamJava 8  introducción a expresiones lambdas y api stream
Java 8 introducción a expresiones lambdas y api stream
 
Algoritmo de enrutamiento por inundación
Algoritmo de enrutamiento por inundaciónAlgoritmo de enrutamiento por inundación
Algoritmo de enrutamiento por inundación
 
Regular Expressions in Java
Regular Expressions in JavaRegular Expressions in Java
Regular Expressions in Java
 
Fundamentos de Programación
Fundamentos de ProgramaciónFundamentos de Programación
Fundamentos de Programación
 
Java interfaces & abstract classes
Java interfaces & abstract classesJava interfaces & abstract classes
Java interfaces & abstract classes
 
Object-oriented concepts
Object-oriented conceptsObject-oriented concepts
Object-oriented concepts
 
Programación Orientada a Objetos - Unidad 2: clases y objetos
Programación Orientada a Objetos - Unidad 2: clases y objetosProgramación Orientada a Objetos - Unidad 2: clases y objetos
Programación Orientada a Objetos - Unidad 2: clases y objetos
 
Lambda Expressions in Java
Lambda Expressions in JavaLambda Expressions in Java
Lambda Expressions in Java
 
Calculo relacional diapositivas
Calculo relacional diapositivasCalculo relacional diapositivas
Calculo relacional diapositivas
 
Sintaxis del lenguaje c++
Sintaxis del lenguaje c++Sintaxis del lenguaje c++
Sintaxis del lenguaje c++
 

Destacado

Destacado (20)

Java 8 - Nuevas características
Java 8 - Nuevas característicasJava 8 - Nuevas características
Java 8 - Nuevas características
 
Portafolio
PortafolioPortafolio
Portafolio
 
Notas de prensa asesinato Colombo
Notas de prensa asesinato ColomboNotas de prensa asesinato Colombo
Notas de prensa asesinato Colombo
 
Cultura chavin
Cultura chavinCultura chavin
Cultura chavin
 
Santa maría
Santa maríaSanta maría
Santa maría
 
Equipo grupos
Equipo gruposEquipo grupos
Equipo grupos
 
Pau resueltas canarias
Pau resueltas canariasPau resueltas canarias
Pau resueltas canarias
 
Adhesión Voluntaria a la Estrategia Aragonesa de Cambio Climático y Energías ...
Adhesión Voluntaria a la Estrategia Aragonesa de Cambio Climático y Energías ...Adhesión Voluntaria a la Estrategia Aragonesa de Cambio Climático y Energías ...
Adhesión Voluntaria a la Estrategia Aragonesa de Cambio Climático y Energías ...
 
Dia del libro IES de La Luz. 2012
Dia del libro IES de La Luz. 2012Dia del libro IES de La Luz. 2012
Dia del libro IES de La Luz. 2012
 
educacion slp
educacion slpeducacion slp
educacion slp
 
Juan Y Borja
Juan Y BorjaJuan Y Borja
Juan Y Borja
 
Medios
MediosMedios
Medios
 
Chance
ChanceChance
Chance
 
Celestin freinet
Celestin freinetCelestin freinet
Celestin freinet
 
Strategy Desk Nov 12
Strategy Desk Nov 12Strategy Desk Nov 12
Strategy Desk Nov 12
 
Les luthiers
Les luthiersLes luthiers
Les luthiers
 
Companerismo
CompanerismoCompanerismo
Companerismo
 
La Energia
La EnergiaLa Energia
La Energia
 
Infografía Informe Tecnocom 2014 - datos comparados
Infografía Informe Tecnocom 2014 - datos comparadosInfografía Informe Tecnocom 2014 - datos comparados
Infografía Informe Tecnocom 2014 - datos comparados
 
Mis Deseos Navidenos
Mis Deseos NavidenosMis Deseos Navidenos
Mis Deseos Navidenos
 

Similar a Lambdas y API Stream - Apuntes de Java

Similar a Lambdas y API Stream - Apuntes de Java (20)

Lambdas y API Stream #PerúJUG #Java20
Lambdas y API Stream #PerúJUG #Java20Lambdas y API Stream #PerúJUG #Java20
Lambdas y API Stream #PerúJUG #Java20
 
Java 8: Expresiones Lambdas y API Stream BarCamp RD 2016
Java 8: Expresiones Lambdas y API Stream BarCamp RD 2016Java 8: Expresiones Lambdas y API Stream BarCamp RD 2016
Java 8: Expresiones Lambdas y API Stream BarCamp RD 2016
 
Kit de supervivencia para Java 8 : como prepararse para Java 9
Kit de supervivencia para Java 8 :  como prepararse para Java 9Kit de supervivencia para Java 8 :  como prepararse para Java 9
Kit de supervivencia para Java 8 : como prepararse para Java 9
 
Java 8
Java 8Java 8
Java 8
 
Functional SE.pdf
Functional SE.pdfFunctional SE.pdf
Functional SE.pdf
 
Java argumentos variables
Java argumentos variablesJava argumentos variables
Java argumentos variables
 
METODO DE SOBRECARGA EN PROGRAMACION.pptx
METODO DE SOBRECARGA EN PROGRAMACION.pptxMETODO DE SOBRECARGA EN PROGRAMACION.pptx
METODO DE SOBRECARGA EN PROGRAMACION.pptx
 
Presentación Java Evolution - GlobalLogic Club
Presentación Java Evolution - GlobalLogic ClubPresentación Java Evolution - GlobalLogic Club
Presentación Java Evolution - GlobalLogic Club
 
Laboratorio interfaces
Laboratorio interfacesLaboratorio interfaces
Laboratorio interfaces
 
Manuales ...
Manuales ...Manuales ...
Manuales ...
 
Introducción a scala
Introducción a scalaIntroducción a scala
Introducción a scala
 
Java Lambda
Java LambdaJava Lambda
Java Lambda
 
sentenciareturnymetodos
sentenciareturnymetodossentenciareturnymetodos
sentenciareturnymetodos
 
Best Practices
Best PracticesBest Practices
Best Practices
 
Java 8: Más funcional que nunca
Java 8: Más funcional que nuncaJava 8: Más funcional que nunca
Java 8: Más funcional que nunca
 
Conceptos fundmentales
Conceptos fundmentalesConceptos fundmentales
Conceptos fundmentales
 
Baño
BañoBaño
Baño
 
Progra
PrograProgra
Progra
 
01 fundamentos-java
01 fundamentos-java01 fundamentos-java
01 fundamentos-java
 
Novedades de Java 8 por PERU JUG
Novedades de Java 8 por PERU JUGNovedades de Java 8 por PERU JUG
Novedades de Java 8 por PERU JUG
 

Lambdas y API Stream - Apuntes de Java

  • 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/