Reputation: 2624
Inspired by Adam Bien's weblog I wanted to replace a common iteration in Java 7 into a nicer 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 too nice. So I replaced it using Adam Bein's example into this:
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 simpler to read, and also introduces an unneeded variable i
, as well as an extra pair of curly brackets. So now I'm wondering if there are other ways to write this code which will make it even nicer and easier to read, primarily using Java 8.
Upvotes: 4
Views: 715
Reputation: 3169
You can also use from predicates:
IntStream.range(0, 10).sorted().filter(i -> i > 5).forEach(System.out::println);
sorted and filer is additional .
Upvotes: 0
Reputation: 132530
As others have pointed out, replacing a simple for-loop with a stream isn't necessarily any better, for this one specific example. However, advantages start to appear if the problem you're trying to solve is more general. For example, instead of repeating some specific code n times, suppose you need to repeat some piece of code that's 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 perhaps
repeat(4, this::doStuff);
Maybe instead performing a single action n times, you want to perform multiple actions n times. You could do something like this:
void repeat(int count, Runnable... actions) {
IntStream.range(0, count).forEach(i -> Arrays.asList(actions).forEach(Runnable::run));
}
Then you'd be able to write:
repeat(5, this::doStuff, this::doMoreStuff, this::doEvenMoreStuff);
The repeat
implementation is somewhat more concise (perhaps terse) than the conventional 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 at the call site, where you can simply pass in a count and one or more lambda expressions or method references, instead of replicating the nested for-loop logic.
Upvotes: 1
Reputation: 298519
Just for completeness, here is a solution which doesn’t need the counter variable:
void repeatUsingJava8(int times) {
Collections.<Runnable>nCopies(times, ()->{
doStuff();
doMoreStuff();
doEvenMoreStuff();
}).forEach(Runnable::run);
}
It would become more readable if there is only one method to be invoked multiple times
as in that case it could be written as, e.g.
void repeatUsingJava8(int times) {
Collections.<Runnable>nCopies(times, this::doStuff).forEach(Runnable::run);
}
If it has to be Stream
s, the code above is equivalent to
void repeatUsingJava8(int times) {
Stream.<Runnable>generate(()->this::doStuff).limit(times).forEach(Runnable::run);
}
However, these alternatives are not really better than the good old for
loop. If you consider parallel
execution, which is a real advantage of Stream
s over ordinary for
loops, there are still alternatives based on commonly known, approved APIs:
ExecutorService es=Executors.newCachedThreadPool();
es.invokeAll(Collections.nCopies(times, Executors.callable(()->{
doStuff();
doMoreStuff();
doEvenMoreStuff();
})));
Upvotes: 5
Reputation: 394016
I don't see any advantage in using Streams for this case. Streams are useful for processing of Collections, arrays and other data structures that contain multiple elements of the same type.
Here you are not processing any data, you just repeat the same action multiple times. There is no reason to replace the good old for loop for that purpose.
Upvotes: 4