En Java 8, vous pouvez utiliser une référence de méthode pour filtrer un flux, par exemple:
Stream<String> s = ...;
long emptyStrings = s.filter(String::isEmpty).count();
Existe-t-il un moyen de créer une référence de méthode qui est la négation d’un existant, c’est-à-dire quelque chose comme:
long nonEmptyStrings = s.filter(not(String::isEmpty)).count();
Je pourrais créer la méthode not
comme ci-dessous, mais je me demandais si le JDK offrait quelque chose de similaire.
static <T> Predicate<T> not(Predicate<T> p) { return o -> !p.test(o); }
Java-11 propose une nouvelle méthode Predicate # not
Vous pouvez donc nier la référence à la méthode:
Stream<String> s = ...;
long nonEmptyStrings = s.filter(Predicate.not(String::isEmpty)).count();
J'envisage d'importer de manière statique les éléments suivants pour permettre à la référence de la méthode d'être utilisée en ligne:
public static <T> Predicate<T> not(Predicate<T> t) {
return t.negate();
}
par exemple.
Stream<String> s = ...;
long nonEmptyStrings = s.filter(not(String::isEmpty)).count();
Mise à jour: - Le JDK/11 pourrait également proposer un solution similaire .
Il existe un moyen de composer une référence de méthode qui est l'opposé d'une référence de méthode actuelle. Voir la réponse de @ vlasec ci-dessous qui montre comment, en convertissant explicitement la référence de la méthode en une variable Predicate
, puis en la convertissant à l'aide de la fonction negate
. C’est un moyen parmi d’autres qui ne sont pas trop gênants.
Le contraire de ceci:
Stream<String> s = ...;
int emptyStrings = s.filter(String::isEmpty).count();
est-ce:
Stream<String> s = ...;
int notEmptyStrings = s.filter(((Predicate<String>) String::isEmpty).negate()).count()
ou ca:
Stream<String> s = ...;
int notEmptyStrings = s.filter( it -> !it.isEmpty() ).count();
Personnellement, je préfère la technique postérieure parce que je trouve plus facile de lire it -> !it.isEmpty()
qu’une longue distribution explicite et verbeuse, puis de nier.
On pourrait aussi faire un prédicat et le réutiliser:
Predicate<String> notEmpty = (String it) -> !it.isEmpty();
Stream<String> s = ...;
int notEmptyStrings = s.filter(notEmpty).count();
Ou, si vous avez une collection ou un tableau, utilisez simplement une boucle for qui est simple, a moins de surcharge et * peut être ** plus rapide:
int notEmpty = 0;
for(String s : list) if(!s.isEmpty()) notEmpty++;
* Si vous voulez savoir ce qui est plus rapide, utilisez JMH http://openjdk.Java.net/projects/code-tools/jmh et évitez le code de référence de la main, à moins que cela n’évite toutes les optimisations JVM - voir Java 8: performances de Streams vs Collections
** On me reproche d'avoir suggéré que la technique de la boucle for est plus rapide. Il élimine la création d'un flux, élimine l'utilisation d'un autre appel de méthode (fonction négative pour le prédicat) et élimine une liste/un compteur d'accumulateur temporaire. Donc, quelques éléments sauvegardés par la dernière construction pourraient le rendre plus rapide.
Je pense que c'est plus simple et plus agréable, même si ce n'est pas plus rapide. Si le travail exige un marteau et un clou, n'apportez pas de scie à chaîne et de colle! Je sais que certains d’entre vous sont en désaccord avec cela.
wish-list: j'aimerais voir les fonctions Java Stream
évoluer un peu maintenant que les utilisateurs de Java les connaissent mieux. Par exemple, la méthode 'count' dans Stream pourrait accepter une Predicate
afin que cela puisse se faire directement comme ceci:
Stream<String> s = ...;
int notEmptyStrings = s.count(it -> !it.isEmpty());
or
List<String> list = ...;
int notEmptyStrings = lists.count(it -> !it.isEmpty());
Predicate
a les méthodes and
, or
et negate
.
Cependant, String::isEmpty
n’est pas une Predicate
, c’est juste un String -> Boolean
lambda et il pourrait encore devenir n'importe quoi, par exemple. Function<String, Boolean>
. L'inférence de type est ce qui doit arriver en premier. La méthode filter
déduit le type implicitement . Mais si vous le niez avant de le passer comme argument, cela ne se produit plus. Comme @axtavt l'a mentionné, explicit inference peut être utilisé de manière laide:
s.filter(((Predicate<String>) String::isEmpty).negate()).count()
Il existe d'autres moyens conseillés dans d'autres réponses, avec la méthode statique not
et lambda étant probablement les meilleures idées. Ceci termine la section tl; dr .
Cependant, si vous souhaitez mieux comprendre l'inférence de type lambda, j'aimerais l'expliquer un peu plus en profondeur, à l'aide d'exemples. Regardez-les et essayez de comprendre ce qui se passe:
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;
Predicate
à Object
- idiot mais validePredicate
en Function
, il ne s'agit plus d'inférencetest
qui est définie par son lambdaInteger
n'a pas de méthode isEmpty
String::isEmpty
avec l'argument Integer
J'espère que cela vous aidera à mieux comprendre le fonctionnement de l'inférence de type.
S'appuyant sur les réponses et l'expérience personnelle des autres:
Predicate<String> blank = String::isEmpty;
content.stream()
.filter(blank.negate())
Une autre option consiste à utiliser le casting lambda dans une classe dans des contextes non ambigus:
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;
}
}
... puis importez de manière statique la classe utilitaire:
stream.filter(as(String::isEmpty).negate())
Ne devrait pas Predicate#negate
être ce que vous cherchez?
Dans ce cas, vous pouvez utiliser le org.Apache.commons.lang3.StringUtils
et faire
int nonEmptyStrings = s.filter(StringUtils::isNotEmpty).count();
Vous pouvez utiliser Prédicats from Collections Eclipse
MutableList<String> strings = Lists.mutable.empty();
int nonEmptyStrings = strings.count(Predicates.not(String::isEmpty));
Si vous ne pouvez pas changer les chaînes de List
:
List<String> strings = new ArrayList<>();
int nonEmptyStrings = ListAdapter.adapt(strings).count(Predicates.not(String::isEmpty));
Si vous avez seulement besoin d'une négation de String.isEmpty()
, vous pouvez également utiliser StringPredicates.notEmpty()
.
Remarque: je contribue aux collections Eclipse.
Vous pouvez accomplir cela aussi longtemps emptyStrings = s.filter(s->!s.isEmpty()).count();
Si vous utilisez Spring Boot (2.0.0+), vous pouvez utiliser:
import org.springframework.util.StringUtils;
...
.filter(StringUtils::hasLength)
...
Qui fait: return (str != null && !str.isEmpty());
Donc, cela aura l'effet de négation requis pour isEmpty