¿Sabías que existe un colador en Java? Sí, parecido a esos que usas en la cocina, o bueno, al menos cumple la misma función. Stream.filter() en Java te ayudará a filtrar elementos que cumplas con ciertos criterios. Voy a explicarte qué es y cuáles son sus caso de uso para que logres un código limpio y reutilizable con este método poderoso.
¿Qué es Stream.filter() en Java?
Te decía que Stream.filter() en Java es como un colador que te ayuda a seleccionar solo los elementos que cumplen con una condición específica dentro de una colección (como una lista). Solo los elementos que pasan la condición se mantienen.
Para definir la condición tienes que usar Predicate, una interfaz funcional que representa una función que devuelve un valor booleano.
En pocas palabras, el método examina cada elemento del stream en Java, aplica el Predicate y conserva solo aquellos elementos que cumplen la condición.
Sintaxis básica
Stream<T> filter(Predicate<? super T> predicate)
Conceptos clave
- Predicate: Esta parte es la lógica de filtrado, ten en cuenta que normalmente está expresada como una lambda o referencia a método.
- Resultado: Aquí verás que se devuelve un nuevo stream con los elementos que cumplen la condición.
Ejemplos prácticos para que aprendas a usar Stream.filter() en Java
Filtra los números pares
Voy a dejarte un ejemplo sencillo para empezar a usar Stream.filter() en Java y que lo vayas entendiendo:
Tendrás que filtrar una lista de números y quedarte solo con los pares.
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class StreamFilterExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
System.out.println(evenNumbers); // Salida: [2, 4, 6]
}
}
En este caso, el Predicate es la condición n -> n % 2 == 0, que comprueba si un número es divisible por 2.
Filtra por propiedades de un objeto
Para este ejemplo quiero que imagines que tienes una lista de empleados y quieres filtrar aquellos cuyo salario supera los 50,000. Hazlo así:
import java.util.Arrays;
import java.util.List;
class Employee {
String name;
double salary;
Employee(String name, double salary) {
this.name = name;
this.salary = salary;
}
@Override
public String toString() {
return name + " - " + salary;
}
}
public class EmployeeFilter {
public static void main(String[] args) {
List<Employee> employees = Arrays.asList(
new Employee("Alice", 60000),
new Employee("Bob", 45000),
new Employee("Charlie", 70000)
);
employees.stream()
.filter(emp -> emp.salary > 50000)
.forEach(System.out::println);
}
}
Salida:
Alice - 60000.0
Charlie - 70000.0
Como ves, usé Stream.filter() para seleccionar empleados con salarios altos y mostrar el resultado en la consola.
Reutilización con métodos estáticos y predicados
Si necesitas reutilizar la lógica de filtrado en diferentes partes de tu código, tienes que definir los predicados estáticos en una clase de utilidades.
import java.util.function.Predicate;
public class EmployeeUtils {
public static Predicate<Employee> salaryGreaterThan(double threshold) {
return emp -> emp.salary > threshold;
}
public static Predicate<Employee> nameStartsWith(String prefix) {
return emp -> emp.name.startsWith(prefix);
}
}
Uso en el stream
employees.stream()
.filter(EmployeeUtils.salaryGreaterThan(50000)
.and(EmployeeUtils.nameStartsWith("A")))
.forEach(System.out::println);
Con esta aproximación, tu código es más modular y fácil de mantener.
Combinación de predicados
Algo grandioso es que puedes combinar múltiples predicados usando los métodos and(), or() o negate() de la interfaz Predicate.
Ejemplo: Combina condiciones
Filtra empleados cuyo salario sea mayor a 50,000 o cuyo nombre empiece con “B”.
employees.stream()
.filter(EmployeeUtils.salaryGreaterThan(50000)
.or(EmployeeUtils.nameStartsWith("B")))
.forEach(System.out::println);
Filtrado con colecciones complejas
Imagina que trabajas con una lista de listas (por ejemplo, grupos de estudiantes) y quieres filtrar solo los estudiantes mayores de edad.
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
class Student {
String name;
int age;
Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return name + " - " + age;
}
}
public class StudentFilter {
public static void main(String[] args) {
List<List<Student>> studentGroups = Arrays.asList(
Arrays.asList(new Student("Alice", 20), new Student("Bob", 17)),
Arrays.asList(new Student("Charlie", 22), new Student("David", 15))
);
List<Student> adultStudents = studentGroups.stream()
.flatMap(List::stream)
.filter(student -> student.age >= 18)
.collect(Collectors.toList());
System.out.println(adultStudents);
}
}
Salida:
[Alice - 20, Charlie - 22]
¿Te diste cuenta del poder de Stream.filter() en Java? Es tan potente que puedes procesar datos de forma eficiente, declarativa y limpia. Aquí te mostré, desde ejemplos sencillos hasta combinaciones complejas de condiciones, que este método es imprescindible para cualquier desarrollador que trabaje con la API de Streams.
¿Quieres aprender más sobre programación funcional y procesamiento de datos en Java? En el Bootcamp de Java Full Stack de KeepCoding, te enseñamos a dominar herramientas como Streams y mucho más para que transformes tu carrera en el sector IT. ¡No esperes más, da el salto hoy mismo!