util - predicate list java
Cómo negar un predicado de referencia de método (12)
En Java 8, puede usar una referencia de método para filtrar una secuencia, por ejemplo:
Stream<String> s = ...;
long emptyStrings = s.filter(String::isEmpty).count();
¿Hay una manera de crear una referencia de método que sea la negación de una existente, es decir, algo como:
long nonEmptyStrings = s.filter(not(String::isEmpty)).count();
Podría crear el método not
como el siguiente, pero me preguntaba si el JDK ofrecía algo similar.
static <T> Predicate<T> not(Predicate<T> p) { return o -> !p.test(o); }
¿No debería ser Predicate#negate
lo que estás buscando?
Aprovechando las respuestas de otros y la experiencia personal:
Predicate<String> blank = String::isEmpty;
content.stream()
.filter(blank.negate())
En este caso, podría usar org.apache.commons.lang3.StringUtils
y hacer
int nonEmptyStrings = s.filter(StringUtils::isNotEmpty).count();
Estoy planeando importar de forma estática lo siguiente para permitir que la referencia del método se use en línea:
public static <T> Predicate<T> not(Predicate<T> t) {
return t.negate();
}
p.ej
Stream<String> s = ...;
long nonEmptyStrings = s.filter(not(String::isEmpty)).count();
Actualización : - Bueno, el JDK / 11 también podría ofrecer una solución similar .
Hay una forma de componer una referencia de método que es lo opuesto a una referencia de método actual. Vea la respuesta de @ vlasec a continuación que muestra cómo al convertir explícitamente la referencia del método a un Predicate
y luego convertirlo usando la función de negate
. Esa es una manera entre otras formas no demasiado problemáticas de hacerlo.
Lo opuesto a esto:
Stream<String> s = ...;
int emptyStrings = s.filter(String::isEmpty).count();
Es esto:
Stream<String> s = ...;
int notEmptyStrings = s.filter(((Predicate<String>) String::isEmpty).negate()).count()
o esto:
Stream<String> s = ...;
int notEmptyStrings = s.filter( it -> !it.isEmpty() ).count();
Personalmente, prefiero la técnica posterior porque me resulta más claro leerla it -> !it.isEmpty()
que una conversión explícita it -> !it.isEmpty()
larga y luego negarla.
También se podría hacer un predicado y reutilizarlo.
Predicate<String> notEmpty = (String it) -> !it.isEmpty();
Stream<String> s = ...;
int notEmptyStrings = s.filter(notEmpty).count();
O, si tiene una colección o matriz, solo use un for-loop que sea simple, tenga menos sobrecarga y * podría ser ** más rápido:
int notEmpty = 0;
for(String s : list) if(!s.isEmpty()) notEmpty++;
* Si desea saber qué es más rápido, use JMH http://openjdk.java.net/projects/code-tools/jmh , y evite el código de referencia manual a menos que evite todas las optimizaciones de JVM - vea Java 8: rendimiento de Streams vs Colecciones
** Recibo críticas por sugerir que la técnica de bucle for es más rápida. Elimina la creación de un flujo, elimina el uso de otro método de llamada (función negativa para predicado) y elimina una lista / contador de acumuladores temporales. Así que algunas cosas que se guardan en la última construcción pueden hacer que sea más rápido.
Creo que es más simple y agradable, aunque no más rápido. Si el trabajo requiere un martillo y un clavo, ¡no traiga una motosierra ni pegamento! Sé que algunos de ustedes están en desacuerdo con eso.
Lista de deseos: me gustaría ver que las funciones de Java Stream
evolucionen un poco ahora que los usuarios de Java están más familiarizados con ellas. Por ejemplo, el método ''contar'' en Stream podría aceptar un Predicate
para que esto se pueda hacer directamente así:
Stream<String> s = ...;
int notEmptyStrings = s.count(it -> !it.isEmpty());
or
List<String> list = ...;
int notEmptyStrings = lists.count(it -> !it.isEmpty());
He escrito una clase de utilidad completa (inspirada en la propuesta de Askar) que puede tomar la expresión lambda de Java 8 y convertirla (si corresponde) en cualquier lambda estándar de Java 8 con tipo definido en el paquete java.util.function
. Usted puede, por ejemplo, hacer:
-
asPredicate(String::isEmpty).negate()
-
asBiPredicate(String::equals).negate()
Debido a que habría muchas ambigüedades si todos los métodos estáticos fueran nombrados solo as()
, opté por llamar al método "como" seguido del tipo devuelto. Esto nos da el control total de la interpretación lambda. A continuación se muestra la primera parte de la clase de utilidad (algo grande) que revela el patrón utilizado.
Echa un vistazo a la clase completa aquí (en la lista).
public class FunctionCastUtil {
public static <T, U> BiConsumer<T, U> asBiConsumer(BiConsumer<T, U> biConsumer) {
return biConsumer;
}
public static <T, U, R> BiFunction<T, U, R> asBiFunction(BiFunction<T, U, R> biFunction) {
return biFunction;
}
public static <T> BinaryOperator<T> asBinaryOperator(BinaryOperator<T> binaryOperator) {
return binaryOperator;
}
... and so on...
}
Otra opción es utilizar el lanzamiento lambda en contextos no ambiguos en una clase:
public static class Lambdas {
public static <T> Predicate<T> as(Predicate<T> predicate){
return predicate;
}
public static <T> Consumer<T> as(Consumer<T> consumer){
return consumer;
}
public static <T> Supplier<T> as(Supplier<T> supplier){
return supplier;
}
public static <T, R> Function<T, R> as(Function<T, R> function){
return function;
}
}
... y luego estática importar la clase de utilidad:
stream.filter(as(String::isEmpty).negate())
Puede lograr esto siempre y cuando emptyStrings = s.filter(s->!s.isEmpty()).count();
Puedes usar Predicates de las Colecciones de Eclipse
MutableList<String> strings = Lists.mutable.empty();
int nonEmptyStrings = strings.count(Predicates.not(String::isEmpty));
Si no puedes cambiar las cadenas de la List
:
List<String> strings = new ArrayList<>();
int nonEmptyStrings = ListAdapter.adapt(strings).count(Predicates.not(String::isEmpty));
Si solo necesitas una negación de String.isEmpty()
también puedes usar StringPredicates.notEmpty()
.
Nota: Soy un colaborador de Eclipse Collections.
Si estás usando Spring Boot (2.0.0+) puedes usar:
import org.springframework.util.StringUtils;
...
.filter(StringUtils::hasLength)
...
Lo que hace: return (str != null && !str.isEmpty());
Así tendrá el efecto de negación requerido para isEmpty
Predicate
tiene métodos and
, or
y negate
.
Sin embargo, String::isEmpty
no es un Predicate
, es solo un String -> Boolean
lambda y aún podría convertirse en cualquier cosa, por ejemplo, Function<String, Boolean>
. La inferencia de tipos es lo que debe suceder primero. El método de filter
infiere el tipo implícitamente . Pero si lo niegas antes de pasarlo como un argumento, ya no sucede. Como se mencionó @axtavt, la inferencia explícita se puede usar como una forma fea:
s.filter(((Predicate<String>) String::isEmpty).negate()).count()
Hay otras formas recomendadas en otras respuestas, con métodos estáticos, not
y lambda que probablemente sean las mejores ideas. Con esto concluye la sección tl; dr .
Sin embargo, si desea una comprensión más profunda de la inferencia de tipo lambda, me gustaría explicarlo un poco más a fondo, utilizando ejemplos. Mira estos y trata de averiguar qué sucede:
Object obj1 = String::isEmpty;
Predicate<String> p1 = s -> s.isEmpty();
Function<String, Boolean> f1 = String::isEmpty;
Object obj2 = p1;
Function<String, Boolean> f2 = (Function<String, Boolean>) obj2;
Function<String, Boolean> f3 = p1::test;
Predicate<Integer> p2 = s -> s.isEmpty();
Predicate<Integer> p3 = String::isEmpty;
- obj1 no compila - lambdas necesita inferir una interfaz funcional (= con un método abstracto)
- p1 y f1 funcionan bien, cada uno de ellos infiere un tipo diferente
- obj2 lanza un
Predicate
toObject
- tonto pero válido - f2 falla en tiempo de ejecución: no se puede convertir
Predicate
toFunction
, ya no se trata de inferencia - f3 funciona: se llama
test
método del predicado que se define por su lambda - p2 no compila -
Integer
no tiene métodoisEmpty
- p3 tampoco compila, no hay un método estático
String::isEmpty
con argumentoInteger
Espero que esto ayude a obtener más información sobre cómo funciona el tipo de referencia.
java-11 ofrece un nuevo método Predicate#not
Así que puedes negar la referencia del método:
Stream<String> s = ...;
long nonEmptyStrings = s.filter(Predicate.not(String::isEmpty)).count();