Upload
alexis-lopez
View
274
Download
0
Embed Size (px)
Citation preview
Perú JUGDIC-2015
Alexis López@aa_lopez
Agenda
Expresiones Lambda
Stream API
Taller Práctico
11
22
33
Expresiones LambdaFunciones como valores/parámetros
<<interface>>ApplePredicate
+test(Apple):boolean
AppleGreenColorPredicate AppleRedColorPredicate
AppleHeavyWeightPredicateAppleRedAndHeavyPredicate
+test(Apple):boolean +test(Apple):boolean
+test(Apple):boolean +test(Apple):boolean
Apple
+getWeight():double+getColor():String
Expresiones LambdaFunciones 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());}
}
...
Código tomado del libro Java 8 in Action
Expresiones LambdaFunciones 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;
}
...
Código tomado del libro Java 8 in Action
Nuestro predicado encapsulala condición
Expresiones LambdaFunciones como valores/parámetros
…
List<Apple> inventory = ...//Separar manzanas por colorList<Apple> greenApples = filterApples(inventory, new
AppleGreenColorPredicate());
//Separar manzanas por pesoList<Apple> heavyApples = filterApples(inventory, new
AppleHeavyWeightPredicate());
...
Código tomado del libro Java 8 in Action
public boolean test(Apple apple){return apple.getWeight() > 150;
}
Necesitaríamos muchas clases...
Expresiones LambdaFunciones como valores/parámetros
…List<Apple> inventory = ...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());
}});
...
Código tomado del libro Java 8 in Action
Enredado, repetido...
Expresiones LambdaFunciones como valores/parámetros
...List<Apple> inventory = …
List<Apple> redApples = filterApples(inventory, (Apple a) -> "red".equals(a.getColor());
List<Apple> greenApples = filterApples(inventory, (Apple a) -> "green".equals(a.getColor()));
...
Código tomado del libro Java 8 in Action
Parametrización de comportamiento
Expresiones LambdaDefinició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 LambdaDefinición
Ejemplos de expresiones lambda:(int a, int b) -> a + ba -> 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 CuerpoOperador lambda
Expresiones LambdaDefiniciones – Interfaces Recargadas - Métodos por Defecto
Definidos e implementados en la Interface.
Implementación por defecto se usa solo cuando la clase implementadora no provee su propia implementación
Para quien invoca, 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 LambdaDefiniciones – Interfaces Recargadas - Métodos por Defecto
En caso de conflictos, este es el orden en el que se selecciona el método por defecto:
1. Implementaciones en clases concretas2. Implementaciones en subinterfaces3. 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 LambdaDefiniciones – Interfaces Recargadas - Métodos por Defecto
public class MultipleInheritance implements MorningInterface{ public static void main(String... args) { MultipleInheritance m = new MultipleInheritance();
m.saludo(); //Buenos días }}
Expresiones LambdaDefiniciones – 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étodo
En este caso es obligatorio implementar el método
Expresiones LambdaDefiniciones – 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…).
@FunctionalInterfaceIndica al compilador que debe verificar si se trata de una interfase funcional.
@FunctionalInterfacepublic 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 LambdaDefiniciones – Interface Funcional – 4 Grandes Grupos
@FunctionalInterfacepublic interface Supplier<T> {
T get();
//Otros métodos...}
@FunctionalInterfacepublic interface Function<T, R> {
R apply(T);
//Otros métodos...}
@FunctionalInterfacepublic interface Predicate<T> {
boolean test(T t);
//Otros métodos...}
@FunctionalInterfacepublic 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 LambdaDefiniciones – 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 LambdaDefiniciones – 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 LambdaDefiniciones – Inferencia de tipos - Casos especiales
//Misma expresión lambda, diferentes interfaces funcionalesCallable<Integer> c = () -> 42;PrivilegedAction<Integer> p = () -> 42;
//void-compatibility ruleList<String> list = ...Predicate<String> p = s -> list.add(s);Consumer<String> c = s -> list.add(s);
Aunque el método abstracto de Consumerretorna void, no hay error de compilación
Expresiones LambdaDefiniciones – Alcance
Efectivamente Constante: Variable o parámetro que solo es asignado una vez.
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ónbefore = 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 LambdaDefiniciones – 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áticosMétodos de Instancia de un objetoMétodos de Instancia de algún tipoConstructores
//Uso para métodos estáticosClase::metodoEstatico//Uso para métodos de instancia de un objeto existenteObjeto::metodoDeInstancia//Uso para métodos de instancia de algún tipo de objetoTipo::metodoDeInstancia//Uso para constructoresClase::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 LambdaDefiniciones – 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 lambdasArrays.sort(personArray, (a, b) -> Person.compareByAge(a, b));
//Ordenar el arreglo usando métodos de referenciaArrays.sort(personArray, Person::compareByAge);
}}
Expresiones LambdaDefiniciones – Métodos de Referencia::Métodos de Instancia de un objeto
public class ComparisonProvider { public int compareByName(Person a, Person b) { return a.getName().compareTo(b.getName());}}...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 lambdasArrays.sort(personArray, (a,b)->myComparisonProvider.compareByName(a, b));
//Ordenar el arreglo usando métodos de referenciaArrays.sort(personArray, myComparisonProvider::compareByName);
}}
Expresiones LambdaDefiniciones – 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 lambdasArrays.sort(stringArray, (a, b) -> a.compareToIgnoreCase(b));
//Ordenar el arreglo usando métodos de referenciaArrays.sort(stringArray, String::compareToIgnoreCase);
}}
Expresiones LambdaDefiniciones – Métodos de Referencia::Constructores
public class Testing{
public static void main(String… args){
//Crear una persona usando lambdasSupplier<Persona> s1 = () -> new Persona();
Persona p1 = s1.get();
//Crear una persona usando métodos de referenciaSupplier<Persona> s1 = Persona::new;Persona p1 = s1.get();
}}
Expresiones LambdaDefiniciones – Métodos de Referencia::Constructores
public class Manzana{private int peso;public Manzana(int w){ peso = w;}
}…
//Crear una manzana usando lambdasFunction<Integer, Manzana> f1 = (w) -> new Manzana(w);Manzana m1 = f1.apply(100);
//Crear una manzana usando métodos de referenciaFunction<Integer, Manzana> f1 = Manzana::new;Manzana m1 = f1.apply(100);
...
Expresiones LambdaDefiniciones – Expresiones Compuestas
Usar métodos por defecto para crear 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 complejosPredicate<String> p1 = s -> s.length() > 3;Predicate<String> p2 = s -> s.charAt(0) == 'A';Predicate<String> p3 = ...
//Precedencia de izquierda a derechaPredicate<String> p4 = p1.or(p2).and(p3).negate();
//Composición de funcionesFunction<String, Integer> f1 = String::length;Function<Integer, Integer> f2 = i -> i * 2;
//Primero aplica f1 y al resultado aplica f2Function<String, Integer> f3 = f1.andThen(f2);
//Composición de comparadoresComparator<String> c1 = (a,b) -> a.length() - b.length();Comparator<String> c2 = String::compareTo;
//Primero compare con c1 y si son iguales use c2Comparator<String> c3 = c1.thenComparing(c2);
Stream APIDefiniciones - 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 APIDefiniciones - Stream
Un Stream está compuesto de tres (3) partes:
1. Una fuente de información2. Cero o más operaciones intermedias3. 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 intermediasOperación terminal
Stream APIDefiniciones - 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”
5. Versiones “primitivas” evitan el Autoboxing y Unboxing ● DoubleStream● IntStream● LongStream
Stream APIDefiniciones – 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 APIOperaciones 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 APIOperaciones 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 APIOperaciones 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.
Map
Reduce
Filter
Stream APIOperaciones 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 APIOperaciones 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
CORRECTO!
Stream<String>
Stream APIOperaciones sobre colecciones de datos - Reduce
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 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 APIOperaciones 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 APIOperaciones 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 APIOperaciones sobre colecciones de datos – Reduce
+reduce(BinaryOperator<T>):Optional<T>Realiza la reducción del Stream usando una función asociativa.
+reduce(T, BinaryOperator<T>):TRealiza la reducción del Stream usando un valor inicial y una función asociativa.
List<Integer> numeros = …
//Obtiene el posible número mayor par del StreamOptional<Integer> opt = numeros
.stream() .filter(x -> x % 2 == 0) .reduce(Integer::max);
//Obtiene la suma de los elementos del StreamInteger suma = numeros
.stream() .reduce(0, (x,y) -> x + y);
Stream APIOperaciones 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: Cuentalong cuenta = numeros.stream().collect(counting());
//Reducción y resúmen: Sumaint suma = numeros
.stream() .collect(summingInt(x -> x.intValue()));
//Reducción y resúmen: Objeto resúmenIntSummaryStatistics res = numeros
.stream()
.collect(summarizingInt(Integer::intValue));
//Reducción y resúmen: Unión de cadenasString csv = numeros.stream().map(Object::toString)
.collect(joining(", "));
summarizingLongsummarizingDouble
summingLongsummingDouble
Stream APIOperaciones sobre colecciones de datos – Reduce - Collectors
Import static java.util.stream.Collectors.*;…List<Empleado> empleados = …
// AgrupamientoMap<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 llavesque tengan valores
Segundo collector permiteagrupar n-niveles
Segundo collector permiteContar la cantidad de elementos
Stream APIOperaciones sobre colecciones de datos – Reduce - Collectors
Import static java.util.stream.Collectors.*;…List<Estudiante> estudiantes = …
//Estudiantes separados entre los que ganaron y perdieron el cursoMap<Boolean, List<Estudiante>> valoracion = estudiantes
.stream() .collect(partitioningBy(s -> s.getNota() >= 3.0));
//Cantidad de estudiantes separados entre los que ganaron y perdieron el cursoMap<Boolean, Long> valoracion = estudiantes
.stream() .collect(partitioningBy(s -> s.getNota() >= 3.0, counting()));
Función de clasificación
Segundo collector permitecontar la cantidad de elementos
particionar n-niveles, etc.
Stream APIDebug
+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.toString()) .map(String::toLowerCase)
.peek(s -> metodoReferencia(s)) .distinct() .collect(Collectors.toList());
Stream APIStreams 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 APIStreams 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 docshttp://download.java.net/jdk8/docs/api/
JDK 8 Downloadhttp://www.oracle.com/technetwork/java/javase/overview/
ArtículosLambdas y API Stream Parte 1Lambdas y API Stream Parte 2
Taller Prácticohttps://github.com/aalopez/lambdas-lab
Java 8 in Actionhttp://www.manning.com/urma/