Introducción
Las expresiones lambda son una nueva característica importante en Java SE 8. Las expresiones lambda le permiten reemplazar interfaces funcionales con expresiones. Una expresión lambda es como un método: proporciona una lista de parámetros normales y un cuerpo (que puede ser una expresión o un bloque de código) que utiliza estos parámetros.
Las expresiones lambda también mejoran la biblioteca de colecciones. Java SE 8 agrega dos paquetes para operaciones por lotes en datos de recopilación: el paquete java.util.function y el paquete java.util.stream. Una secuencia es como un iterador, pero con muchas características adicionales. En general, las expresiones y secuencias lambda son los mayores cambios desde la adición de genéricos y anotaciones al lenguaje Java. En este artículo, veremos el poder de las expresiones y flujos lambda, desde ejemplos simples hasta complejos.
Preparación ambiental
Si no se ha instalado Java 8, debe instalarlo primero antes de poder usar lambda y stream (el traductor recomienda instalarlo en una máquina virtual para realizar pruebas). Herramientas e IDE como NetBeans e IntelliJ IDEA admiten funciones de Java 8, incluidas expresiones lambda, anotaciones repetibles, perfiles compactos y otras funciones.
Sintaxis de expresión lambda
Sintaxis básica:
(parámetros) -> expresión
o
(parámetros) ->{ declaraciones }
A continuación se muestra un ejemplo sencillo de una expresión lambda de Java:
Copie el código de código de la siguiente manera:
// 1. No se requieren parámetros, el valor de retorno es 5
() -> 5
// 2. Recibe un parámetro (tipo numérico) y devuelve 2 veces su valor
x -> 2*x
// 3. Acepta 2 parámetros (números) y devuelve su diferencia
(x, y) -> xy
// 4. Recibe 2 enteros de tipo int y devuelve su suma
(int x, int y) -> x + y
// 5. Acepta un objeto de cadena e imprímelo en la consola sin devolver ningún valor (parece que devuelve void)
(Cadena s) -> System.out.print(s)
Ejemplo básico de Lambda
Ahora que sabemos qué son las expresiones lambda, comencemos con algunos ejemplos básicos. En esta sección, veremos cómo las expresiones lambda afectan la forma en que codificamos. Supongamos que hay una lista de jugadores, el programador puede usar una declaración for ("bucle for") para recorrerla, que se puede convertir a otra forma en Java SE 8:
Copie el código de código de la siguiente manera:
Cadena[] atp = {"Rafael Nadal", "Novak Djokovic",
"Estanislao Wawrinka",
"David Ferrer","Roger Federer",
"Andy Murray","Tomás Berdych",
"Juan Martín Del Potro"};
Lista<String> jugadores = Arrays.asList(atp);
//Método de bucle anterior
para (Jugador de cadena: jugadores) {
System.out.print(jugador + "; ");
}
//Utiliza expresiones lambda y operaciones funcionales
jugadores.forEach((jugador) -> System.out.print(jugador + "; "));
//Usar operador de dos puntos dobles en Java 8
jugadores.forEach(System.out::println);
Como puede ver, las expresiones lambda pueden reducir nuestro código a una línea. Otro ejemplo son los programas de interfaz gráfica de usuario donde las clases anónimas pueden reemplazarse por expresiones lambda. De manera similar, también se puede usar así al implementar la interfaz Runnable:
Copie el código de código de la siguiente manera:
//Usar clase interna anónima
btn.setOnAction(nuevo EventHandler<ActionEvent>() {
@Anular
identificador de vacío público (evento ActionEvent) {
System.out.println("¡Hola mundo!");
}
});
// O usar expresión lambda
btn.setOnAction(evento -> System.out.println("¡Hola mundo!"));
El siguiente es un ejemplo del uso de lambdas para implementar la interfaz Runnable:
Copie el código de código de la siguiente manera:
// 1.1 Usar clases internas anónimas
nuevo hilo (nuevo ejecutable() {
@Anular
ejecución pública vacía() {
System.out.println("¡Hola mundo!");
}
}).comenzar();
// 1.2 Usar expresión lambda
new Thread(() -> System.out.println("¡Hola mundo!")).start();
// 2.1 Usar clases internas anónimas
Carrera ejecutable1 = nueva Ejecutable() {
@Anular
ejecución pública vacía() {
System.out.println("¡Hola mundo!");
}
};
// 2.2 Usar expresión lambda
Carrera ejecutable2 = () -> System.out.println("¡Hola mundo!");
// Llama al método de ejecución directamente (¡no se abre ningún hilo nuevo!)
carrera1.run();
carrera2.run();
La expresión lambda de Runnable utiliza formato de bloque para convertir cinco líneas de código en una declaración de una sola línea. A continuación, en la siguiente sección usaremos lambdas para ordenar la colección.
Ordenar colecciones usando Lambdas
En Java, la clase Comparator se utiliza para ordenar colecciones. En el siguiente ejemplo, nos basaremos en el nombre, apellido, longitud del nombre y última letra del jugador. Como en el ejemplo anterior, primero usamos una clase interna anónima para ordenar y luego usamos expresiones lambda para simplificar nuestro código.
En el primer ejemplo, ordenaremos la lista por nombre. Usando la forma antigua, el código se vería así:
Copie el código de código de la siguiente manera:
String[] jugadores = {"Rafael Nadal", "Novak Djokovic",
"Stanislas Wawrinka", "David Ferrer",
"Roger Federer", "Andy Murray",
"Tomás Berdych", "Juan Martín Del Potro",
"Richard Gasquet", "John Isner"};
// 1.1 Usa clases internas anónimas para ordenar jugadores según su nombre
Arrays.sort(jugadores, nuevo Comparador<String>() {
@Anular
public int comparar (Cadena s1, Cadena s2) {
retorno (s1.compareTo(s2));
}
});
Usando lambdas, se puede lograr la misma funcionalidad con el siguiente código:
Copie el código de código de la siguiente manera:
// 1.2 Usa expresión lambda para ordenar jugadores
Comparador<Cadena> sortByName = (Cadena s1, Cadena s2) -> (s1.compareTo(s2));
Arrays.sort(jugadores, sortByName);
// 1.3 también puede adoptar la siguiente forma:
Arrays.sort(jugadores, (String s1, String s2) -> (s1.compareTo(s2)));
Otras clasificaciones son las siguientes. Como en el ejemplo anterior, el código implementa Comparator a través de clases internas anónimas y algunas expresiones lambda:
Copie el código de código de la siguiente manera:
// 1.1 Usa clases internas anónimas para ordenar jugadores según el apellido
Arrays.sort(jugadores, nuevo Comparador<String>() {
@Anular
public int comparar (Cadena s1, Cadena s2) {
return (s1.substring(s1.indexOf(" ")).compareTo(s2.substring(s2.indexOf(" "))));
}
});
// 1.2 Usar expresión lambda para ordenar según apellido
Comparador<Cadena> sortBySurname = (Cadena s1, Cadena s2) ->
( s1.substring(s1.indexOf(" ")).compareTo( s2.substring(s2.indexOf(" ")) ) );
Arrays.sort(jugadores, sortBySurname);
// 1.3 O así, me pregunto si el autor original se equivocó, hay tantos corchetes...
Arrays.sort(jugadores, (Cadena s1, Cadena s2) ->
( s1.substring(s1.indexOf(" ")).compareTo( s2.substring(s2.indexOf(" ")) ) )
);
// 2.1 Usar clases internas anónimas para ordenar jugadores según la longitud del nombre
Arrays.sort(jugadores, nuevo Comparador<String>() {
@Anular
public int comparar (Cadena s1, Cadena s2) {
retorno (s1.length() - s2.length());
}
});
// 2.2 Usar expresión lambda para ordenar según la longitud del nombre
Comparador<Cadena> sortByNameLenght = (Cadena s1, Cadena s2) -> (s1.length() - s2.length());
Arrays.sort(jugadores, sortByNameLenght);
// 2.3 o esto
Arrays.sort(jugadores, (String s1, String s2) -> (s1.length() - s2.length()));
// 3.1 Usa una clase interna anónima para ordenar jugadores según la última letra
Arrays.sort(jugadores, nuevo Comparador<String>() {
@Anular
public int comparar (Cadena s1, Cadena s2) {
return (s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1));
}
});
// 3.2 Usar expresión lambda para ordenar según la última letra
Comparador<Cadena> sortByLastLetter =
(Cadena s1, Cadena s2) ->
(s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1));
Arrays.sort(jugadores, sortByLastLetter);
// 3.3 o esto
Arrays.sort(jugadores, (String s1, String s2) -> (s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1)));
Eso es todo, simple e intuitivo. En la siguiente sección exploraremos más capacidades de lambdas y las usaremos con transmisiones.
Usando Lambdas y Streams
Stream es un contenedor para colecciones y generalmente se usa junto con lambda. El uso de lambdas puede admitir muchas operaciones, como mapear, filtrar, limitar, ordenar, contar, mínimo, máximo, suma, recopilar, etc. De manera similar, Streams usa operaciones diferidas, en realidad no leen todos los datos y la sintaxis de la cadena finaliza cuando encuentra métodos como getFirst (). En los siguientes ejemplos, exploraremos lo que pueden hacer lambdas y streams. Creamos una clase Persona y usamos esta clase para agregar algunos datos a la lista, que se usarán para futuras operaciones de transmisión. Persona es solo una clase POJO simple:
Copie el código de código de la siguiente manera:
Persona de clase pública {
cadena privada nombre, apellido, trabajo, sexo;
salario int privado, edad;
Persona pública (cadena nombre, cadena apellido, cadena trabajo,
Género de cadena, edad int, salario int) {
this.firstName = firstName;
this.apellido = apellido;
this.gender = genero;
this.age = edad;
este.trabajo = trabajo;
this.salary = salario;
}
// captador y definidor
// .
}
A continuación, crearemos dos listas, ambas utilizadas para almacenar objetos Persona:
Copie el código de código de la siguiente manera:
Lista<Persona> javaProgramadores = nueva ArrayList<Persona>() {
{
add(nueva Persona("Elsdon", "Jaycob", "programador Java", "hombre", 43, 2000));
add(new Person("Tamsen", "Brittany", "programador Java", "mujer", 23, 1500));
add(nueva Persona("Floyd", "Donny", "programador Java", "hombre", 33, 1800));
add(nueva Persona("Sindy", "Jonie", "programador Java", "mujer", 32, 1600));
add(new Person("Vere", "Hervey", "programador Java", "masculino", 22, 1200));
add(new Person("Maude", "Jaimie", "programador Java", "mujer", 27, 1900));
add(nueva Persona("Shawn", "Randall", "programador Java", "hombre", 30, 2300));
add(nueva Persona("Jayden", "Corrina", "programador Java", "mujer", 35, 1700));
add(new Person("Palmer", "Dene", "programador Java", "hombre", 33, 2000));
add(nueva Persona("Addison", "Pam", "programador Java", "mujer", 34, 1300));
}
};
Lista<Persona> phpProgrammers = nueva ArrayList<Persona>() {
{
add(new Person("Jarrod", "Pace", "programador PHP", "masculino", 34, 1550));
add(nueva Persona("Clarette", "Cicely", "programadora PHP", "mujer", 23, 1200));
add(nueva Persona("Victor", "Channing", "programador PHP", "hombre", 32, 1600));
add(nueva Persona("Tori", "Sheryl", "programadora PHP", "mujer", 21, 1000));
add(nueva Persona("Osborne", "Shad", "programador PHP", "hombre", 32, 1100));
add(nueva Persona("Rosalind", "Layla", "programadora PHP", "mujer", 25, 1300));
add(new Person("Fraser", "Hewie", "programador PHP", "masculino", 36, 1100));
add(nueva Persona("Quinn", "Tamara", "programador PHP", "mujer", 21, 1000));
add(nueva Persona("Alvin", "Lance", "programador PHP", "hombre", 38, 1600));
add(new Person("Evonne", "Shari", "programador PHP", "mujer", 40, 1800));
}
};
Ahora usamos el método forEach para iterar y generar la lista anterior:
Copie el código de código de la siguiente manera:
System.out.println("Nombres de todos los programadores:");
javaProgrammers.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
phpProgrammers.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
También utilizamos el método forEach para aumentar el salario del programador en un 5%:
Copie el código de código de la siguiente manera:
System.out.println("Otorgue a los programadores un aumento salarial del 5%:");
Consumidor<Persona> GiveRaise = e -> e.setSalary(e.getSalary() / 100 * 5 + e.getSalary());
javaProgrammers.forEach(darAumentar);
phpProgrammers.forEach(darAumentar);
Otro método útil es filter(), que nos permite mostrar programadores PHP con un salario mensual superior a $1400:
Copie el código de código de la siguiente manera:
System.out.println("Aquí hay programadores PHP con un salario mensual de más de $1,400:")
phpProgramadores.stream()
.filtro((p) -> (p.getSalary() > 1400))
.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
También podemos definir filtros y luego reutilizarlos para realizar otras operaciones:
Copie el código de código de la siguiente manera:
//definir filtros
Predicado<Persona> ageFilter = (p) -> (p.getAge() > 25);
Predicado<Persona> filtrosalario = (p) -> (p.getSalary() > 1400);
Predicado<Persona> GenderFilter = (p) -> ("female".equals(p.getGender()));
System.out.println("Aquí hay programadoras PHP que tienen más de 24 años y un salario mensual de más de $1,400:");
phpProgramadores.stream()
.filter(filtroedad)
.filter(filtrosalario)
.filter(filtro de género)
.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
//Reutilizar filtros
System.out.println("Programadoras Java mayores de 24 años:");
javaProgramadores.stream()
.filter(filtroedad)
.filter(filtro de género)
.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
Utilice el método de límite para limitar el número de conjuntos de resultados:
Copie el código de código de la siguiente manera:
System.out.println("Los primeros 3 programadores de Java:");
javaProgramadores.stream()
.límite(3)
.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
System.out.println("Las 3 mejores programadoras de Java:");
javaProgramadores.stream()
.filter(filtro de género)
.límite(3)
.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
¿Qué pasa con la clasificación? ¿Podemos manejarlo en la transmisión? En el siguiente ejemplo, ordenaremos a los programadores de Java por nombre y salario, los colocaremos en una lista y luego mostraremos la lista:
Copie el código de código de la siguiente manera:
System.out.println("Ordenar por nombre y mostrar los 5 mejores programadores de Java:");
Lista<Persona> ordenadoJavaProgrammers = javaProgrammers
.arroyo()
.sorted((p, p2) -> (p.getFirstName().compareTo(p2.getFirstName())))
.límite(5)
.collect(toList());
ordenadoJavaProgrammers.forEach((p) -> System.out.printf("%s %s; %n", p.getFirstName(), p.getLastName()));
System.out.println("Ordenar programadores Java según salario:");
ordenadosJavaProgramadores = javaProgramadores
.arroyo()
.sorted( (p, p2) -> (p.getSalary() - p2.getSalary()) )
.collect(toList());
ordenadoJavaProgrammers.forEach((p) -> System.out.printf("%s %s; %n", p.getFirstName(), p.getLastName()));
Si solo estamos interesados en los salarios más bajos y más altos, lo que es más rápido que seleccionar el primero/último después de ordenar son los métodos mínimo y máximo:
Copie el código de código de la siguiente manera:
System.out.println("El programador Java peor pagado:");
Persona pers = javaProgramadores
.arroyo()
.min((p1, p2) -> (p1.getSalary() - p2.getSalary()))
.conseguir()
System.out.printf("Nombre: %s %s; Salario: $%,d.", pers.getFirstName(), pers.getLastName(), pers.getSalary())
System.out.println("Programador Java con el salario más alto:");
Persona persona = javaProgramadores
.arroyo()
.max((p, p2) -> (p.getSalary() - p2.getSalary()))
.conseguir()
System.out.printf("Nombre: %s %s; Salario: $%,d.", persona.getFirstName(), person.getLastName(), person.getSalary())
En el ejemplo anterior hemos visto cómo funciona el método de recopilación. Junto con el método de mapa, podemos usar el método de recopilación para colocar nuestro conjunto de resultados en una Cadena, un Conjunto o un TreeSet:
Copie el código de código de la siguiente manera:
System.out.println("Concatenar el nombre de los programadores de PHP en una cadena:");
Cadena phpDesarrolladores = phpProgramadores
.arroyo()
.map(Persona::getFirstName)
.collect(joining(" ; ")); // Puede usarse como token en operaciones posteriores.
System.out.println("Guarde el nombre de los programadores de Java en Set:");
Set<String> javaDevFirstName = javaProgramadores
.arroyo()
.map(Persona::getFirstName)
.collect(toSet());
System.out.println("Guarde el nombre de los programadores de Java en TreeSet:");
TreeSet<String> javaDevLastName = javaProgramadores
.arroyo()
.map(Persona::obtenerApellido)
.collect(toCollection(TreeSet::nuevo));
Las corrientes también pueden ser paralelas. Los ejemplos son los siguientes:
Copie el código de código de la siguiente manera:
System.out.println("Calcular todo el dinero pagado a los programadores de Java:");
int salario total = javaProgramadores
.parallelStream()
.mapToInt(p -> p.getSalario())
.suma();
Podemos utilizar el método resumenStatistics para obtener varios datos resumidos de los elementos de la secuencia. A continuación, podemos acceder a estos métodos como getMax, getMin, getSum o getAverage:
Copie el código de código de la siguiente manera:
//Calcular el recuento, mínimo, máximo, suma y promedio de números
Lista<Integer> números = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
IntSummaryStatistics estadísticas = números
.arroyo()
.mapToInt((x) -> x)
.summaryStatistics();
System.out.println("El número más grande en la lista: " + stats.getMax());
System.out.println("El número más pequeño de la lista: " + stats.getMin());
System.out.println("Suma de todos los números: " + stats.getSum());
System.out.println("Promedio de todos los números: " + stats.getAverage());
Ok, eso es todo, ¡espero que les guste!
Resumir
En este artículo, aprendimos diferentes formas de usar expresiones lambda, desde ejemplos básicos hasta ejemplos más complejos que usan lambdas y transmisiones. Además, también aprendimos cómo usar expresiones lambda y la clase Comparator para ordenar colecciones de Java.
Nota del traductor: aunque parece muy avanzada, la esencia de una expresión Lambda es simplemente un "azúcar de sintaxis" que el compilador infiere y le ayuda a convertirlo y envolverlo en código normal, de modo que pueda usar menos código para lograr la misma función. . Recomiendo no usarlo indiscriminadamente, porque es como el código escrito por algunos piratas informáticos muy avanzados. Es conciso, difícil de entender y de depurar, y el personal de mantenimiento querrá regañarlo.