How to create good iterations in Java 8

Inspired by the Adam Bien weblog I wanted to replace the usual iteration in Java 7 with the more pleasant one in Java 8. The older code looked like this:

void repeatUsingJava7(int times) { for (int i = 0; i < times; i++) { doStuff(); doMoreStuff(); doEvenMoreStuff(); } } 

... which is not very nice. So I replaced it using the example of Adam Bean:

 void repeatUsingJava8(int times) { IntStream.range(0, times).forEach( i -> { doStuff(); doMoreStuff(); doEvenMoreStuff(); } ); } 

... which is a step in the right direction, but does not make the code much easier to read, and also introduces an unnecessary i variable, as well as an extra pair of curly braces. So now I'm wondering if there are other ways to write this code that will make it even more enjoyable and understandable, primarily using Java 8.

+4
java iteration java-8
source share
4 answers

Just for completeness, here is a solution that does not need a counter variable:

 void repeatUsingJava8(int times) { Collections.<Runnable>nCopies(times, ()->{ doStuff(); doMoreStuff(); doEvenMoreStuff(); }).forEach(Runnable::run); } 

It would become more readable if only one method with several times was called, because in this case it could be written, for example,

 void repeatUsingJava8(int times) { Collections.<Runnable>nCopies(times, this::doStuff).forEach(Runnable::run); } 

If it should be Stream s, then the above code is equivalent

 void repeatUsingJava8(int times) { Stream.<Runnable>generate(()->this::doStuff).limit(times).forEach(Runnable::run); } 

However, these alternatives are actually no better than the old old for loop. If you consider parallel execution, which is the real advantage of Stream over regular for loops, there are still alternatives based on well-known approved APIs:

 ExecutorService es=Executors.newCachedThreadPool(); es.invokeAll(Collections.nCopies(times, Executors.callable(()->{ doStuff(); doMoreStuff(); doEvenMoreStuff(); }))); 
+5
source share

I do not see any advantage in using Streams for this case. Streams are useful for processing Collections, arrays, and other data structures that contain multiple elements of the same type.

Here you do not process any data, you just repeat the same action several times. There is no reason to replace the good old cycle for this purpose.

+4
source share

As others have noted, replacing a simple for loop with a thread is not necessarily better for this specific example. However, benefits begin to emerge if the problem you are trying to solve is more general. For example, instead of repeating some specific code n times, suppose you need to repeat part of the code passed as a parameter? Consider:

 void repeat(int count, Runnable action) { IntStream.range(0, count).forEach(i -> action.run()); } 

Now you can write

 repeat(3, () -> System.out.println("Hello!")); 

or maybe

 repeat(4, this::doStuff); 

Perhaps instead of performing one action n times, you want to perform several actions n times. You can do something like this:

 void repeat(int count, Runnable... actions) { IntStream.range(0, count).forEach(i -> Arrays.asList(actions).forEach(Runnable::run)); } 

Then you can write:

 repeat(5, this::doStuff, this::doMoreStuff, this::doEvenMoreStuff); 

The repeat implementation is somewhat shorter (perhaps shorter) than the usual Java 7 way:

 void repeatOldWay(int count, Runnable...actions) { for (int i = 0; i < count; i++) { for (Runnable r : actions) { r.run(); } } } 

The real advantage comes from the call site, where you can simply pass the score and one or more lambda expressions or method references instead of replicating the logic of nested loops.

+1
source share

You can also use from predicates:

 IntStream.range(0, 10).sorted().filter(i -> i > 5).forEach(System.out::println); 

sorted and filer are optional.

0
source share

All Articles