ejemplo - ¿Cómo aplicar predicados múltiples a java.util.Stream?
stream filter java predicate (4)
¿Cómo puedo aplicar predicados múltiples a un método de filter()
java.util.Stream''s
?
Esto es lo que hago ahora, pero realmente no me gusta. Tengo una Collection
de cosas y necesito reducir el número de cosas en función de la Collection
de filtros (predicados):
Collection<Thing> things = someGenerator.someMethod();
List<Thing> filtered = things.parallelStream().filter(p -> {
for (Filter f : filtersCollection) {
if (f.test(p))
return true;
}
return false;
}).collect(Collectors.toList());
Sé que si supiera la cantidad de filtros por adelantado, podría hacer algo como esto:
List<Thing> filtered = things.parallelStream().filter(filter1).or(filter2).or(filter3)).collect(Collectors.toList());
Pero, ¿cómo puedo aplicar una cantidad desconocida de predicados sin mezclar estilos de programación? Por saber que se ve algo feo ...
Esta es una forma interesante de resolver este problema (pegando directamente desde http://www.leveluplunch.com/java/tutorials/006-how-to-filter-arraylist-stream-java8/ ). Creo que esta es una forma más eficiente.
Predicate<BBTeam> nonNullPredicate = Objects::nonNull;
Predicate<BBTeam> nameNotNull = p -> p.teamName != null;
Predicate<BBTeam> teamWIPredicate = p -> p.teamName.equals("Wisconsin");
Predicate<BBTeam> fullPredicate = nonNullPredicate.and(nameNotNull)
.and(teamWIPredicate);
List<BBTeam> teams2 = teams.stream().filter(fullPredicate)
.collect(Collectors.toList());
EDITAR: Aquí es cómo tratar con los bucles donde predicatesToIgnore es una lista de predicados. Creo un solo predicado predicado ToIgnore a partir de él.
Predicate<T> predicateToIgnore = null;
for (Predicate<T> predicate : predicatesToIgnore) {
predicateToIgnore = predicateToIgnore == null ? predicate : predicateToIgnore.or(predicate);
}
Luego, haz un filtro con este único predicado. Esto crea un mejor filtro en mi humilde opinión
He logrado resolver ese problema, si un usuario desea aplicar una lista de predicados en una operación de filtro, una lista que puede ser dinámica y no está dada, que debe reducirse a un predicado, como este:
public class TestPredicates {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
System.out.println(numbers.stream()
.filter(combineFilters(x -> x > 2, x -> x < 9, x -> x % 2 == 1))
.collect(Collectors.toList()));
}
public static <T> Predicate<T> combineFilters(Predicate<T>... predicates) {
Predicate<T> p = Stream.of(predicates).reduce(x -> true, Predicate::and);
return p;
}
}
Tenga en cuenta que esto los combinará con un operador lógico "Y". Para combinar con un "O", la línea de reducción debe ser:
Predicate<T> p = Stream.of(predicates).reduce(x -> false, Predicate::or);
Si tiene un Collection<Predicate<T>> filters
, siempre puede crear un único predicado utilizando el proceso llamado reduction :
Predicate<T> pred=filters.stream().reduce(Predicate::and).orElse(x->true);
o
Predicate<T> pred=filters.stream().reduce(Predicate::or).orElse(x->false);
dependiendo de cómo quiera combinar los filtros.
Si la reserva para una colección de predicados vacía especificada en la llamada orElse
cumple la función de identidad (que x->true
hace para and
los predicados x->false
hace para or
ing) también puede usar reduce(x->true, Predicate::and)
o reduce(x->false, Predicate::or)
para obtener el filtro, pero eso es ligeramente menos eficiente para colecciones muy pequeñas, ya que siempre combinaría el predicado de identidad con el predicado de la colección, incluso si contiene solo un predicado . Por el contrario, la variante reduce(accumulator).orElse(fallback)
muestra arriba devolverá el predicado único si la colección tiene el tamaño 1
.
Observe cómo este patrón también se aplica a problemas similares: al tener una Collection<Consumer<T>>
, puede crear un solo Consumer<T>
usando
Consumer<T> c=consumers.stream().reduce(Consumer::andThen).orElse(x->{});
Etc.
Supongo que su Filter
es un tipo distinto de java.util.function.Predicate
, lo que significa que debe adaptarse. Un enfoque que funcionará es el siguiente:
things.stream().filter(t -> filtersCollection.stream().anyMatch(f -> f.test(t)));
Esto genera un ligero golpe de rendimiento al recrear el flujo de filtro para cada evaluación de predicado. Para evitar eso, puede envolver cada filtro en un Predicate
y componerlos:
things.stream().filter(filtersCollection.stream().<Predicate>map(f -> f::test)
.reduce(Predicate::or).orElse(t->false));
Sin embargo, como ahora cada filtro está detrás de su propio Predicate
, introduciendo un nivel más de indirección, no está claro qué enfoque tendría un mejor rendimiento general.
Sin la preocupación de adaptación (si su Filter
pasa a ser un Predicate
) la declaración del problema se vuelve mucho más simple y el segundo enfoque gana claramente:
things.stream().filter(
filtersCollection.stream().reduce(Predicate::or).orElse(t->true)
);