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.
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.
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
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
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.
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);
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.
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
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
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.
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.
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.
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.
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;
}
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();
}
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();
}
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.
c'est tout. C'était une bonne occasion d'essayer ce qui m'intéressait.
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