Lamdas Flashcards
Que es una expresion lambda
Es una interfaz que puede o no estat anotada con @FunctionalInterface, si lo esta el compilador verificara que cumpla con las siguientes reglas:
- Solo debe de constar de un metodo abstracto
- Adicional puede contar con cero o muchos metodos estaticos
- cero o muchas metodos default,
Debemos considerar que los metodos propios de la clase Object se excluyen de estas reglas, es decir no cuentan como metodos de una functional interface, aunque no debemos de modificarlos, una lamda no debe modificar los metodos heredados de Object
Como se podia hacer el comportamiento de una lambda antes de java 8
Por medio de implementar una clase anonima
Cual es la ventaja de las expresiones lamdas comparado con las clases anonimas
Las lamdas son:
- faciles de leer y de escribir
- se pueden pasar como funcionalidad en muchos lugares como si fuera un metodo comun pero pasamos funcionalidad en lugar de datos u objetos
- Se procesan en paralelo si tenemos un ambiente multicore
Cuales son los pasos para pasar una clase anonima a una lamda normal
- Primero tomamos la seccion de los parametros, estos pueden ser cero o varios parametros, basicamente todo lo que esta entre parentesis, opcionalmente podemos omitir el tipado de los parametros ya que como al ser una interfaz funcional solo tiene un metodo y con eso el compilador puede inferir el tipo sin que nosotros lo especifiquemos
- Ponemos el simbolo -> esta es la notacion que usan las lamdas
- Tomamos todo lo que esta entre llaves {} removiendo el return si aplica, si tiene mas de una linea tenemos que ponerlo dentro de {} si solo es una linea podemos omitir las {}
En que lugar se pueden ocupar lambdas
En parametros de entrada y como tipos de salida(return)
Que tipo de objeto es una lamda
Es un objeto sin identidad, la diferencia entre una clase anonima y yna lamda es que para crear una clase anonima necesitamos usar new, lo cual implica que debemos realizar todo el proceso de inicializacion de un objeto comun y corriente
- Memoria
- CleanUp
- ejecutar inicializadores estaticos
- bloques estaticos
- inicilizadores no estaticos
- no estatic blocks
- constructor
y con una expresion lamda no, en ese sentido es mas eficiente utilizar lamdas ya que no tenemos ese overhead
Cuales son los tipos de lamdas
- Supplier: no toma ningun objeto y retorna una nueva instancia de T, ejemplo: T get()
- Consumer: acepta un objeto y no regresa nada, ejemplo: void accept(T t)
- Bi-Consumer: acepta dos objetos y no regresa nada, ejemplo void accept(T t, R r)
- Predicate: Toma un objeto y nos regresa un boleano, ejemplo: boolean test(T t)
- Bi-Predicate: acepta dos objetos y regresa un boleano, ejemplo boolean test(T t, R r)
- Function: Toma un objeto y regresa un objeto de otro tipo, ejemplo: R apply(T t)
- Bi-Function: Toma dos objetos y nos regresa otro de otro tipo, ejemplo; R apply(T t,R r)
- UnaryOperator: Recibe un objeto y regresa otro del mismo tipo, ejemplo R accept(R r)
- BinaryOperator: Recibe 2 objetos y regresa otro del mismo tipo, ejemplo R accept(R r, R r)
Con que otra notacion se pueden crear las lamdas
Se llama method reference y es un shortcut los pasos son los siguientes
Omitir la parte de los parametros, es decir los parentesis()
Ponemos parte de los simbolos ->
Llamamos el metodo por medio de :: ejemplo System.out::println
Un ejemplo quedaria asi Comparator s = num -> Integer::compare
Cabe señalar que NO soportan argumentos por ejemplo m.age>20 se tendria que hacer con la notacion normal
Cuales son los metodos default y metodos estaticos en Java 8
Son metodos los cuales podemos o no tener en una interfaz funcional y loque se hace es que se implementan directamente en la interfaz, esto se realizo asi debido a que de esta manera se no se rompe la backward compatibility, imaginemos que agregabamos un metodo a la interfaz List, esto haria que todas nuestras versiones tuvieran que implementarlo, al hacer metodos default nos permite agregar ese metodo y tenerlo sin necesidad de implementarlo y romper el contrato, al igual podemos tener cero o muchos metodos estaticos en una interfaz y se comportara de la misma manera que un metodo estatico en una clase
Describe el patron map/filter/reduce
Es un patron que nos permite procesar nuestra informacion, imaginemos que tenemos una lista de personas que tengan entre 20 y 25 años entre las personas contenidas en la lista,la descripcion seria la siguiente:
- Map: en esta paso vamos a pasar la lista de personas a una lista de enteros por medio de su edad, el chiste de este paso es que si tenemos 10 elementos persona en la lista, lo mapemos a 10 elementos enteros (este paso a veces es omitido y se realiza directamente en el filtrado)
- Filter: una vez que tenemos la lista de enteros los filtramos, quitando las personas que no cumplan con nuestro predicado, en este caso si teniamos 10 enteros, podemos acabar con menos dependiendo de si cumplen o no el predicado, pero aqui se puede reducir
- Reduce: esto lo podemos ver como un tipo agregation de sql, por ejemplo sumar todos los resultantes, sacar el promedio, etc para al final solo terminar con un numero que represente nuestro requerimiento
Que es un stream
Tecnicamente es una interfaz generica,
- se utilizan para el procesamiento de la informacion
- No se deben de modificar(la info que se esta procesando) ya que estan pensadas solo para lectura y no escritura auqnue nada nos lo impude
- Liberan el poder del procesamiento multicore y paralelismo
- Todo se hace en un solo pipelane, es decir que no existen operaciones intermedias, solo se tienen que encadenar
- No almacenan informacion por lo cual no tendra operaciones intermedias
- Nos ayuda a implementar el patron map/filter/reduce
- Es una api nueva que se puede usar por ejemplo en colecciones
- No se puede reutilizsar un stream, es decir si ya su ultimo elemento de la cadena es una operacion terminal, ya no se puede reutilizar
Si un stream no almacena informacion, entonces que es lo que pasa cuando un paso intermedio dentro de un map/filter/reduce regresa un stream?
No pasa nada, en realidad es solo una marca que indica que debe de continuar la ejecucion del siguiente paso, es decir es un paso intermedio y en el momento de que el siguiente paso es una operacion terminal, ese sera nuestro trigger para ejecutar todos los pasos, pero mientras haya pasos intermedios (que regresen un stream) continuarra ejecutando los pasos siguientes
Como podemos identificar una operacion final y una operacion intermedia
- Las operaciones intermedias nos regresan un stream
- Las operaciones finales nos regresan void o otra cosa que no sea stream
Cual es la diferencia entre un map y un flatmap
supongamos que tenemos las siguientes listas
a=[1,2,3,4]
b=[5,6]
c=[7,8,9]
en map seria asi [[1,2,3,4], [5,6], [7,8,9]]
en flatmap seria asi: [1,2,3,4,5,6,7,8,9]
***A que se refieran los optionals
- Es un wrapper como Integer a int pero este no puede estar vacio
- Que nos puede o no regresar algo,
Cuales son los tipos de reducciones
Reducciones de agregation(max, count, allMatch,noneMatch, anyMatch, findFirst, findAny)
Reducciones con operaciones terminales (reduce)
Que don los reducciones mutables
Es cuando queremos reducir una lsita a un mapa
Que caracteristicas tienen los parametros en una lamda
- Podemos omitir el tipo
- Se puede usar el operador final en los parametros para que no sea modificado
- Podemos usar anotaciones
4.
En genericos cual es la diferencia cuando tenemos un metodo estatico y uno de instancia
Cuando es un metodo no estatico(de instancia) utilizamos la misma variable que se definio como generica en la clase
Si es un metodo estatico debemos definir otro generico
Ejemplo
public class Comparator<t>{</t>
private T compareOne(T other){
……
}
public static compareStatic(U u){
}
}
Como se crean las nuevas APIs
De debe de hacer uso de lamdas, metodos default y metodos estaticos introducidos en java 8
Como se obtiene de una lista un stream
la lista definida ya tiene un metodo default llamado stream el cual nos va a permitir procesar la informacion .stream()
De que otra manera se pueden construir streams
Por medio de metodos estaticos
Stream.empty()
Stream.of(“one”,…)—Se puede especificar uno o muchos elementos dentro de los parentesis
Stream.generate(()->“one”)—De esta manera sera un stream constante, es decir que cada vez que pidamos a ese stream nos va a regresar siempre el valor “one”
Stram.iterate(“+”, s -> s + “+”)—En ese caso, va a generar un valor de agregacion, es decir va a ir contatenando las “+” por medio del metodo limit() podemos especificar el numero de elementos generados, ya que por default no tiene limite y lo hara infinitamente
ThreadLocaRandom.current().ints()—En este caso creara valores enteros de manera aleatoria, current tiene mas metodos relacionados a cada primitivo, por ejemplo Double etc
Tambien podemos crear streams por medio de expresiones regulares
En archivos podemos crear streams que lean la linea completa
Describe el builder para construir un stream
Se hace por añaden elementos por medio de los siguientes metodos:
- add( Nos regresa un Stream, por lo cual podemos encadenar todos los elementos que necesitemos)
- accept(regresa void y por lo tanto no podemos encadenar los elementos, seria añarlos uno a uno )
- Una vez que terminamos de armar nuestro stream, tenemos que llamar al metodo .build, cabe señalar que una vez que llamamos a este metodo no podemos llamar a ninguno de los que añaden, de lo contrario nos va a mandar una excepcion
Como podemos trabajar con primitivos y lamndas
Existen algunos metodos casados con los tipos primitivos, en este caso nos regresara valores primitivos y no objetos
IntPredicate
IntFunction
IntToDobleFunction
Describe algunos metodos que se agregaron a la interfaz map
forEach
putIfAbstent: si la llave ya esta en el mapa no hace nada de lo contrario la añade
getOtDefault: antes si deciamos get y no estaba mandaba null, ahora tenemos esta notacion la cual sera una manera de que si no existe, inicialicemos la variable por medio de un valor por dafault asociado con el tipo del valor
replace: ahora recibe dos o tres parametros,(key, valor nuevo) o key, valor existente, valor nuevo
remove: especificamos llave y valor, solo si ambos coinciden se remueve el mapa de lo contrario no
compute: vamos a computar algo por medio de computeIfAbsent o conputeIfPresent, dependemos de que la conficion se cumpla para que se ejecute, si no se cumple no se vuelve a ejecutar, por ejemplo si esta vacio el mapa inicializalo con un default y agregale algun elemento, si ya existe omite esta parte y solo añade el elemento
merge
Cuales son los problemas conocidos con las lamdas en un ambiente multicore
- Asociatividad: las operaciones deben de cumplir esta regla matematica, la cual nos dice que el orden de los parentesis no altara el resultado, es decir (5+5)+2= 12 es igual a 5 + (5+2)=12, si la operacion es la misma no cambia, pero si ponemos diferentes operaciones en una misma expresion ya no es igual (5+5)x2=20 ya no es igual si decimos 5 + (5*2)=15, esto se debe de cumplir ya que en un ambiente multicore el trabajo puede realizarse en paralelo y un core pude tratar de resolver una parte de nuestra lamda y otro core otra, y al final juntar ambos resultados y reducirlo a un solo resultado, es por eso que debe de ser asociativo para que el resultado no sea alterado en una ambiente multicore
- Si las operaciones no tienen un tipo de identidad, por ejemplo si queremos obtener el maximo de un set de numeros pero hay negativos y positivos e indicamos la identidad como 0, el resultado seria incorrecto
Cual es la manera de incorrecta de hacer el map/filter/reduce
La manera incorrecta seria por ejemplo tener 3 metodos, supongamos que tenemos una lista de un millon de elementos
- Mapeamos de personas a enteros el millos y lo asignamos a una variable
- Pasamos la variable con el millon de enteros y lo filtramos dada una condicion y lo asignamos a otra variable, supongamos que se filtra a la mitad(medio millon de elementos)
- La variable con medio millon la pasamo al metodo reduce y lo reducimos a el promedio de empleados menores a 30 años
Como al tener operaciones intermedias es poco eficiente ya que tenemos un millon de personas de inicio, despues un millon de enteros, despues medio millon y al final un numero, tenemos un overhead y duplicamos informacion con las operaciones intermedias
Como se seleccionan rangos en una stream
Por medio de
- limit: le indicamos el numero de elementos que queremos nos regrese ej stream().limit(3)
- skip, le indicamos que se salte cierto numero de elementos y empiece a procesar a partir del siguiente skip(2), es no inclusivo;
Describe algunos metodos para reducir streams
Los siguientes metodos necesitan que se cumpla un predicado, cuentan con mecanismos de corto circuito, donde si el predicado falla antes ya no se ejecutaran las demas, regresan un boleano
- anyMatch(): cualquiera debe de matchear
- noneMatch(): ninguno de matchear de lo contrario regresa false
- allMatch(): todos deben de matchear con el predicado de lo contrario regresa false
Los siguentes dependen que se cumpla un predicado y nos van a regresar un opcional ya que si esta vacia la lista o no se cumple el predicado nos deberian regresar algo
- findFirst:
- findAny
Tambien tenemos reducciones generales
- reduce: con identidad
- reduce: sin identidad
- reduce: para paralelismo
Describe el metodo reduce en lambdas
Reduce es una operacion terminal, la cual acepta dos parametros, en una de sus firmas, el primero es la identidad, esto quiere decir que es el primer valor contra el que se va a interpretar la primera vez y el segundo es una operacion binaria
Ejemplo
suponiendo que la lista es la siguiente:
List<integer> numbers = Arrays.asList(3, 6, 34, 23);</integer>
return numbers.stream()
// a = 1(identity), b = 3(first element on the list) = 3
// a = 3(last result), b = 6(second element on the list) = 18
// a = 18(last result), b = 34(third element on the list) = 612
// a = 612(last result), b = 23(forth element on the list) = 14076
.reduce(1, ((a, b) -> a * b));