[JAVA] J'ai essayé ce que je voulais essayer avec Stream doucement.

C'est normal de participer au calendrier de l'Avent d'une manière ou d'une autre, mais je n'ai rien à écrire, alors je vais parler de Stream, que je peux écrire. Le code que j'ai essayé cette fois est également stocké dans GitHub.

Aspect de lisibilité

Ce gars a ajouté dans Java 8. Comme beaucoup d'entre vous le savent peut-être déjà, si vous essayez de comparer et de mesurer le temps d'exécution, si les éléments à gérer sont petits, le temps d'exécution sera plus lent que le type procédural. L'avantage est que le nom de la méthode est clair et qu'il est facile de comprendre ce que la source veut faire.

Environnement d'exécution de référence simple

Je ne veux pas utiliser d'outils ou évaluer le nombre de fois jusqu'à 1 million Il semble que vous puissiez utiliser nanoTime, mais j'ai choisi currentTimeMillis parce que je n'ai besoin que de connaître la différence.

Bench.java


public class Main {
    public static void main(String[] args) {
        System.out.println("Excution time:"+benchMark()+"[sec]");
    }

    private static double benchMark(){
        long start = System.currentTimeMillis();
        HogeSomeTask task = new HogeSomeTask();
        task.something_do();
        long end = System.currentTimeMillis();

        return ((end - start) / 1000.0);
    }
}

OS:Ubuntu 16.04 CPU: Processeur Intel Core i7-2700K à 5,9 GHz (veuillez pardonner l'ancien) JDK:Open-jdk9

Exemple de code

Le code suivant est le résultat d'une analyse comparative avec le code du processus pour générer les données cibles de moins de 5 caractères comme condition. Au contraire, j'ai également essayé le processus de sortie de données de 5 caractères ou plus, mais le résultat était presque le même, alors je l'ai omis.

Procedural.java


    public void use_for(){ 
        List<String>list = Arrays.asList("Java","Ruby","Csharp","Scala","Haskell");
        for(String lang : list){
            if(lang.length() < 5){
                System.out.println(lang);
            }
        }
    } 

Le temps d'exécution moyen pour 10 fois est de 0,001 [s]. Assez vite

Utiliser Stream

Stream.java


 public void use_stream(){ 
        List<String>list = Arrays.asList("Java","Ruby","Csharp","Scala","Haskell");
        list.stream().filter(lang -> lang.length() < 5).forEach(System.out::println);
 }

Le temps d'exécution moyen pour 10 fois est de 0,025 [s]. C'est un peu plus lent que le type procédural.

Avec Java9, Stream a également de nouvelles fonctionnalités

Cela ressemble plus à ça maintenant, mais jetons un coup d'œil à la méthode. Il peut être écrit comme Scala par la méthode take / dropWhile. takeWhile Une méthode qui peut traiter les données cibles (alors que les conditions sont remplies) simplement en spécifiant les conditions cibles. Le traitement intermédiaire a été réduit.

takeWhileExample.java


   List<String>list = Arrays.asList("Java","Ruby","Csharp","Scala","Haskell")   
         list.stream().takeWhile(lang -> lang.length() < 5).forEach(System.out::println);

dropWhile Une méthode qui peut générer des données cibles (une fois que les conditions sont remplies) simplement en spécifiant les conditions cibles. Comme pour takeWhile, le traitement intermédiaire a été réduit.

dropWhileExample.java


  List<String> list = Arrays.asList("Java","Ruby","Csharp","Scala","Haskell");
        list.stream().dropWhile(lang -> lang.length() < 5).forEach(System.out::println);

ofNullable Si les données cibles ne sont pas nulles, un Stream est renvoyé. Si null, une méthode qui renvoie un Stream vide. Vous pouvez maintenant écrire des flux directement à partir de l'option facultative comme indiqué ci-dessous

optional.java


Optional.ofNullable(null).stream().forEach(System.out::println);

La vitesse qui vous tient à cœur

J'ai tout essayé et je l'ai évalué, alors je l'ai résumé dans un tableau. En ce qui concerne ofNullable, il semble qu'il y ait un gros point à gérer null en toute sécurité, donc cette fois je vérifie les performances de traitement des données, je vais l'omettre car il n'y a pas de temps pour ad-care.

1. Traitement de l'exemple de code ci-dessus

Temps d'exécution moyen(10 fois)
Type procédural 0.001[sec]
Stream 0.025[sec]
parallelStream 0.026[sec]
takeWhile 0.026[sec]
takeWhile(Utiliser parallelStream) 0.032[sec]

Il n'y a pas de grande différence par rapport à certaines méthodes de Java8. Le traitement intermédiaire semble lent

2. Traitement inverse de 1

Temps d'exécution moyen(10 fois)
Type procédural 0.001[sec]
Stream 0.023[sec]
parallelStream 0.031[sec]
dropWhile 0.024[sec]
dropWhile(Utiliser parellelStream) 0.028[sec]

Comme ci-dessus

Raisons de perdre au type procédural

En gros, dans le type procédural, le traitement est écrit presque tel quel et compilé avec jdk, donc même si les données sont simples, le Stream qui effectue le traitement intermédiaire semble être lent.

Supplément

Je veux clarifier les raisons d'être en retard. Pour cela, j'ai essayé de suivre le processus écrit dans la méthode Stream avec la fonction IntelliJ. Il est divisé en petits appels de méthode et le mécanisme tel que l'exécution différée est ralenti.

Y a-t-il un moment où Stream est rapide?

Quand l'écrire, il suffit de sélectionner Stream qui est facile à lire, et même s'il est lent, la différence n'est pas si grande. Cependant, en termes de performances, je l'ai essayé car il semble efficace lorsque les éléments à manipuler sont volumineux.

conditions

En tant que données de test, créez 1 million d'éléments d'une chaîne de caractères aléatoires composée de 20 lettres majuscules et de chiffres.

BigData.java


Random r = new Random(2111);
List<String> data = range(0, 1_000_000)
    .mapToObj(i->
        r.ints().limit(20)
            .map(n -> Math.abs(n) % 36)
            .map(code -> (code < 10) ? '0' + code : 'A' + code - 10)
            .mapToObj(ch -> String.valueOf((char)ch))
            .toString())
    .collect(toList());

Seuls les nombres sont extraits de cet élément et le total est de 30 ou moins.

Échantillon de procédure

Procedural.java


  public static long use_for(List<String> data){
        long result = 0;
        for(String d : data){
            String numOnly = d.replaceAll("[^0-9]", "");
            if(numOnly.isEmpty()) continue;
            int total = 0;
            for(char ch : numOnly.toCharArray()){
                total += ch - '0';
            }
            if(total >= 30) continue;
            long value = Long.parseLong(numOnly);
            result += value;
        }
        return result;
    }

Échantillon de flux

Stream.java


 public static long streamSum(List<String>data){
        return data.stream()
                .map(d -> d.replaceAll("[^0-9]", ""))
                .filter(d -> !d.isEmpty())
                .filter(d -> d.chars().map(ch -> ch - '0').sum() < 30)
                .mapToLong(d -> Long.parseLong(d)).sum();
 }

Essayez également takeWhile / dropWhile

Je suis curieux de savoir comment il sera comparé à simplement stream et parallelStream, alors je vais l'essayer.

takeWhileSample.java


  public static long takeWhileSum(List<String> data){
        return data.stream()
                .map(d -> d.replaceAll("[^0-9]", ""))//Supprimer les non-nombres
                .takeWhile(d -> !d.isEmpty())
                .takeWhile(d -> d.chars().map(ch -> ch - '0').sum() < 30)//La somme des nombres est inférieure à 30
                .mapToLong(d -> Long.parseLong(d)).sum();
  }

dropWhileSample


    public static long dropWhileSum(List<String> data){
        return data.stream()
                .map(d -> d.replaceAll("[^0-9]", ""))
                .dropWhile(d -> d.isEmpty())
                .dropWhile(d -> d.chars().map(ch -> ch - '0').sum() > 30)
                .mapToLong(d -> Long.parseLong(d)).sum();
   }

Résultats de référence

Temps d'exécution moyen(10 fois)
Type procédural 2.132[sec]
parallelStream 1.321[sec]
Stream 2.107[sec]
takeWhile 0.457[sec]
takeWhile(parallelStream) 1.325[sec]
dropWhile 2.175[sec]
dropWhile(parallelStream) 1.377[sec]

J'ai l'impression d'avoir enfin vu la vraie valeur. takeWhile est de loin le plus rapide. Le dropWhile était à peu près le même que le type procédural, mais l'appeler depuis un parallelStream le rendait beaucoup mieux.

Conclusion

c'est tout. C'était une bonne occasion d'essayer ce qui m'intéressait.

Articles référencés

Objectif de Java 8 Stream, facilité d'écriture, lisibilité et effet du traitement parallèle Mesurez le résultat de l'exécution du programme avec C ++, Java, Python.

Tel.

Recommended Posts

J'ai essayé ce que je voulais essayer avec Stream doucement.
J'ai essayé d'interagir avec Java
J'ai essayé de démarrer avec Web Assembly
J'ai essayé de résumer l'API Stream
Qu'est-ce que Docker? J'ai essayé de résumer
J'ai essayé de vérifier AdoptOpenJDK 11 (11.0.2) avec l'image Docker
J'ai essayé de faire une authentification de base avec Java
J'ai essayé de gérer la configuration des jambes de force avec Coggle
J'ai essayé de gérer les informations de connexion avec JMX
Je voulais classer la botte à ressort dans un multi-projet
J'ai essayé de casser le bloc avec java (1)
Ce que j'ai essayé quand je voulais obtenir tous les champs d'un haricot
J'ai essayé d'implémenter le téléchargement de fichiers avec Spring MVC
J'ai essayé de lire et de sortir CSV avec Outsystems
J'ai essayé d'implémenter TCP / IP + BIO avec JAVA
[Java 11] J'ai essayé d'exécuter Java sans compiler avec javac
J'ai démarré MySQL 5.7 avec docker-compose et j'ai essayé de me connecter
J'ai essayé de démarrer avec Spring Data JPA
J'ai essayé de dessiner une animation avec l'API Blazor + canvas
J'ai essayé d'implémenter Sterling Sort avec Java Collector
J'ai essayé de découvrir ce qui avait changé dans Java 9
J'ai essayé DI avec Ruby
J'ai essayé node-jt400 (flux SQL)
J'ai essayé UPSERT avec PostgreSQL.
J'ai essayé BIND avec Docker
J'ai essayé de vérifier yum-cron
J'ai essayé de créer un environnement de développement java8 avec Chocolatey
J'ai essayé de moderniser une application Java EE avec OpenShift.
J'ai essayé d'augmenter la vitesse de traitement avec l'ingénierie spirituelle
[Rails] J'ai essayé de créer une mini application avec FullCalendar
J'ai essayé de lier le chat avec le serveur de Minecraft avec l'API Discord
[Rails] J'ai essayé d'implémenter le traitement par lots avec la tâche Rake
Ce à quoi j'étais accro avec l'API REST Redmine
J'ai essayé de créer un environnement de développement padrino avec Docker
J'ai essayé de démarrer avec Swagger en utilisant Spring Boot
J'ai essayé de pouvoir passer plusieurs objets avec Ractor
J'ai essayé de résoudre le problème de la "sélection multi-étapes" avec Ruby
J'ai essayé de mâcher C # (indexeur)
J'ai essayé de résumer ce qui était demandé lors de l'édition site-java-
J'ai essayé d'utiliser JOOQ avec Gradle
J'ai essayé l'analyse morphologique avec MeCab
J'ai essayé de créer un environnement de serveur UML Plant avec Docker
J'ai essayé de me connecter à MySQL en utilisant le modèle JDBC avec Spring MVC
J'ai essayé de résumer le support d'iOS 14
J'ai essayé d'implémenter la fonction de prévisualisation d'image avec Rails / jQuery
J'ai essayé de créer un environnement de développement http2 avec Eclipse + Tomcat
J'ai essayé d'implémenter un mappage OU flexible avec MyBatis Dynamic SQL
J'ai essayé la communication UDP avec Java
J'ai essayé d'expliquer la méthode
L'histoire que je voulais développer Zip
Ce à quoi j'étais accro lors de la mise en œuvre de l'authentification Google avec des rails
J'ai essayé de réimplémenter Ruby's Float (arg, exception: true) avec builtin