Je viens de tomber sur une question lors de l'utilisation de a List
et sa stream()
méthode. Bien que je sache comment les utiliser, je ne sais pas trop quand les utiliser.
Par exemple, j'ai une liste, contenant divers chemins vers différents endroits. Maintenant, j'aimerais vérifier si un seul chemin donné contient l'un des chemins spécifiés dans la liste. Je voudrais retourner un boolean
basé sur le fait que la condition a été remplie ou non.
Ce n'est bien sûr pas une tâche difficile en soi. Mais je me demande si je devrais utiliser des flux ou une boucle for (-each).
La liste
private static final List<String> EXCLUDE_PATHS = Arrays.asList(new String[]{
"my/path/one",
"my/path/two"
});
Exemple - Stream
private boolean isExcluded(String path){
return EXCLUDE_PATHS.stream()
.map(String::toLowerCase)
.filter(path::contains)
.collect(Collectors.toList())
.size() > 0;
}
Exemple - Pour chaque boucle
private boolean isExcluded(String path){
for (String excludePath : EXCLUDE_PATHS) {
if(path.contains(excludePath.toLowerCase())){
return true;
}
}
return false;
}
Notez que le path
paramètre est toujours en minuscules .
Ma première hypothèse est que l'approche pour chaque est plus rapide, car la boucle reviendrait immédiatement, si la condition est remplie. Alors que le flux bouclerait toujours sur toutes les entrées de la liste afin de terminer le filtrage.
Mon hypothèse est-elle correcte? Si oui, pourquoi (ou plutôt quand ) utiliserais-je stream()
alors?
la source
new String[]{…}
ici. Juste utiliserArrays.asList("my/path/one", "my/path/two")
String[]
, il n'est pas nécessaire d'appelerArrays.asList
. Vous pouvez simplement diffuser sur le tableau en utilisantArrays.stream(array)
. En passant, j'ai du mal à comprendre le but duisExcluded
test dans son ensemble. Est-il vraiment intéressant de savoir si un élément deEXCLUDE_PATHS
est littéralement contenu quelque part dans le chemin? IeisExcluded("my/path/one/foo/bar/baz")
reviendratrue
, ainsi queisExcluded("foo/bar/baz/my/path/one/")
…Arrays.stream
méthode, merci de l'avoir signalé. En effet, l'exemple que j'ai posté semble tout à fait inutile pour quelqu'un d'autre que moi. Je suis conscient du comportement de laisExcluded
méthode, mais c'est vraiment juste quelque chose dont j'ai besoin pour moi, donc, pour répondre à votre question: oui , c'est intéressant pour des raisons que je ne voudrais pas mentionner, car cela ne rentrerait pas dans le champ d'application de la question initiale.toLowerCase
appliqué à la constante qui est déjà en minuscules? Ne devrait-il pas s'appliquer à l'path
argument?Réponses:
Votre hypothèse est correcte. Votre implémentation de flux est plus lente que la boucle for.
Cette utilisation du flux devrait être aussi rapide que la boucle for:
Cela parcourt les éléments, appliquant
String::toLowerCase
et le filtre aux éléments un par un et se terminant au premier élément qui correspond.Les deux
collect()
&anyMatch()
sont des opérations de terminal.anyMatch()
se termine au premier élément trouvé, cependant, alorscollect()
que tous les éléments doivent être traités.la source
findFirst()
en combinaison avecfilter()
. Apparemment, je ne sais pas comment utiliser les flux aussi bien que je le pensais.La décision d'utiliser ou non Streams ne doit pas être motivée par des considérations de performances, mais plutôt par la lisibilité. Quand il s'agit vraiment de performances, il y a d'autres considérations.
Avec votre
.filter(path::contains).collect(Collectors.toList()).size() > 0
approche, vous traitez tous les éléments et les collectez dans unList
, avant de comparer la taille, cela n'a presque jamais d'importance pour un Stream composé de deux éléments.L'utilisation
.map(String::toLowerCase).anyMatch(path::contains)
peut économiser des cycles CPU et de la mémoire, si vous avez un nombre d'éléments sensiblement plus grand. Pourtant, cela convertit chacunString
dans sa représentation en minuscules, jusqu'à ce qu'une correspondance soit trouvée. De toute évidence, il y a un intérêt à utiliserau lieu. Vous n'avez donc pas besoin de répéter la conversion en lowcase à chaque invocation de
isExcluded
. Si le nombre d'élémentsEXCLUDE_PATHS
ou la longueur des chaînes devient vraiment important, vous pouvez envisager d'utiliserCompiler une chaîne en tant que modèle regex avec le
LITERAL
drapeau, la fait se comporter comme des opérations de chaîne ordinaires, mais permet au moteur de passer un certain temps à se préparer, par exemple en utilisant l'algorithme de Boyer Moore, pour être plus efficace lorsqu'il s'agit de la comparaison réelle.Bien sûr, cela ne paie que s'il y a suffisamment de tests ultérieurs pour compenser le temps passé à la préparation. Déterminer si ce sera le cas est l'une des considérations de performances réelles, outre la première question de savoir si cette opération sera un jour critique en termes de performances. Pas la question de savoir s'il faut utiliser Streams ou
for
boucles.En passant, les exemples de code ci-dessus conservent la logique de votre code d'origine, ce qui me semble discutable. Votre
isExcluded
méthode renvoietrue
, si le chemin spécifié contient l'un des éléments de la liste, elle renvoie donctrue
pour/some/prefix/to/my/path/one
, ainsi quemy/path/one/and/some/suffix
ou même/some/prefix/to/my/path/one/and/some/suffix
.Même
dummy/path/onerous
est considéré comme remplissant les critères car c'estcontains
la chaînemy/path/one
…la source
Ouais. Vous avez raison. Votre approche de flux aura des frais généraux. Mais vous pouvez utiliser une telle construction:
La principale raison d'utiliser les flux est qu'ils rendent votre code plus simple et facile à lire.
la source
anyMatch
un raccourci pourfilter(...).findFirst().isPresent()
?L'objectif des flux en Java est de simplifier la complexité de l'écriture de code parallèle. Il est inspiré de la programmation fonctionnelle. Le flux série est juste pour rendre le code plus propre.
Si nous voulons des performances, nous devons utiliser parallelStream, qui a été conçu pour. La série, en général, est plus lente.
Il y a un bon article à lire , et la performance
ForLoop
Stream
ParallelStream
.Dans votre code, nous pouvons utiliser des méthodes de terminaison pour arrêter la recherche sur la première correspondance. (anyMatch ...)
la source
Comme d'autres l'ont mentionné de nombreux points positifs, mais je veux juste mentionner l' évaluation paresseuse dans l' évaluation de flux. Lorsque nous
map()
créons un flux de chemins en minuscules, nous ne créons pas tout le flux immédiatement, au lieu de cela, le flux est construit paresseusement , c'est pourquoi les performances devraient être équivalentes à la boucle for traditionnelle. Il ne fait pas une analyse complètemap()
etanyMatch()
sont exécutés en même temps. Une foisanyMatch()
renvoyé vrai, il sera court-circuité.la source