113 votes

Quand devrais-je utiliser des flux?

Je viens de tomber sur une question lors de l'utilisation d'un List et son stream() méthode. Même si je sais comment les utiliser, je ne suis pas tout à fait sûr de savoir quand les utiliser.

Par exemple, j'ai une liste, contenant des chemins différents à des endroits différents. Maintenant, j'aimerais vérifier si un seul, étant donné le chemin d'accès contient des chemins d'accès indiqués dans la liste. J'aimerais revenir un boolean basée sur si la condition est remplie.

Ce n'est bien sûr pas une tâche difficile en soi. Mais je me demande si je ne devrais pas utiliser les flux, ou pour (chaque) en boucle.

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 l' path paramètre est toujours en minuscules.

Ma première hypothèse est que le pour-chaque approche est plus rapide, parce que la boucle de retour immédiatement, si la condition est remplie. Alors que le flux serait encore en boucle sur toutes les entrées de la liste afin de filtrage complet.

Est mon hypothèse est correcte? Si oui, pourquoi (ou plutôt quand) puis-je utiliser stream() , alors?

90voto

Stefan Pries Points 1047

Votre hypothèse est correcte. Votre flux de mise en œuvre est plus lente que la boucle for.

Cette utilisation de flux doit être aussi rapide que la boucle for:

EXCLUDE_PATHS.stream().map(String::toLowerCase).anyMatch(path::contains);

Ce parcourt les éléments, en appliquant String::toLowerCase et le filtre pour les éléments un par un et se terminant sur le premier élément qui correspond.

Les deux collect() & anyMatch() sont des opérations de terminal. anyMatch() quitte à le premier élément trouvé, cependant, tandis que d' collect() exige que tous les articles à traiter.

35voto

Holger Points 13789

La décision d'utiliser les Flux ou pas ne devrait pas être entraîné par le facteur de performance, mais plutôt par des raisons de lisibilité. Quand il s'agit vraiment de la performance, il y a d'autres considérations.

Avec votre .filter(path::contains).collect(Collectors.toList()).size() > 0 approche, vous êtes de traitement de tous les éléments et de les collecter dans un temporaire List, avant de comparer la taille, la encore, ce presque jamais de questions pour un Flux composé de deux éléments.

À l'aide de .map(String::toLowerCase).anyMatch(path::contains) pouvez économiser des cycles CPU et de la mémoire, si vous avez un considérablement plus grand nombre d'éléments. Encore, cela transforme chaque String de ses minuscules représentation, jusqu'à ce qu'une correspondance soit trouvée. Évidemment, il y a un point à l'aide de

private static final List<String> EXCLUDE_PATHS =
    Stream.of("my/path/one", "my/path/two").map(String::toLowerCase)
          .collect(Collectors.toList());

private boolean isExcluded(String path) {
    return EXCLUDE_PATHS.stream().anyMatch(path::contains);
}

au lieu de cela. Si vous n'avez pas à répéter la conversion au bien celle à chaque invocation de la isExcluded. Si le nombre d'éléments dans EXCLUDE_PATHS ou les longueurs des cordes devient vraiment grand, vous pouvez envisager d'utiliser

private static final List<Predicate<String>> EXCLUDE_PATHS =
    Stream.of("my/path/one", "my/path/two").map(String::toLowerCase)
          .map(s -> Pattern.compile(s, Pattern.LITERAL).asPredicate())
          .collect(Collectors.toList());

private boolean isExcluded(String path){
    return EXCLUDE_PATHS.stream().anyMatch(p -> p.test(path));
}

La compilation d'une chaîne de caractères comme une expression régulière pattern avec l' LITERAL drapeau, la fait se comporter comme de simples opérations de la chaîne, mais il permet au moteur de passer du temps dans la préparation, p. ex. à l'aide de la Boyer Moore algorithme, pour être plus efficace quand il s'agit de la comparaison réelle.

Bien sûr, ce n'est rentable que si il y a assez des tests ultérieurs pour compenser le temps passé en préparation. Déterminer si ce sera le cas, est l'un des réels facteurs de performance, en plus de la première question de savoir si cette opération sera jamais critique pour les performances à tous. Pas la question de savoir si l'utilisation ou de Ruisseaux for boucles.

Par ailleurs, les exemples de code ci-dessus garder la logique de votre code d'origine, ce qui semble douteux pour moi. Votre isExcluded méthode renvoie true, si le chemin d'accès spécifié contient un des éléments dans la liste, afin qu'il renvoie true pour /some/prefix/to/my/path/one, ainsi qu' my/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 que c' contains la chaîne my/path/one...

22voto

rvit34 Points 789

Ouais. Tu as raison. Votre approche de flux aura des frais généraux. Mais vous pouvez utiliser une telle construction:

 private boolean isExcluded(String path) {
    return  EXCLUDE_PATHS.stream().map(String::toLowerCase).anyMatch(path::contains);
}
 

La principale raison d'utiliser des flux est qu'ils rendent votre code plus simple et facile à lire.

12voto

Le but de flux en Java est de simplifier la complexité de l'écriture de code parallèle. Elle est inspirée par la programmation fonctionnelle. La série de flux est juste pour rendre le code plus propre.

Si nous voulons de la performance, nous devrions l'utiliser parallelStream, qui a été conçu pour. La série, en général, est plus lent.

Il y a un bon article à lire sur ForLoop, Stream et ParallelStream de Rendement.

Dans votre code, nous pouvons utiliser une terminaison méthodes pour arrêter la recherche sur le premier match. (anyMatch...)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X