Loop vs Stream
Before addressing your question, I want to emphasize that loops and Streams are not always interchangeable.
In some cases, loops can be converted into Streams. But it doesn't mean that a Stream is just a fancy form of a loop. These are elements of two different programming paradigms.
With loops, you have a tight full control over the iteration process. Break earlier whenever you need, even jump using labels (although it's not a recommended practice, to use labeled statements). You dictate how to traverse, loops are external iterators. Also, side effects are good buddies of loops. It's very common to manipulate with external state, mutate elements of the structure you're iterating over, etc. That's the world of imperative programming, dictatorship expressed in the form of code.
On the other hand, Stream is an internal iterator, i.e. you're not managing the process of the iteration directly. Also, since streams are functional programming constructs you're not supposed to mutate stream elements (yes, it's a side effect), manipulate with the state outside the stream, etc.
Side effects, especially in the intermediate operations, are discouraged by the Stream API documentation:
Side-effects in behavioral parameters to stream operations are, in general, discouraged, as they can often lead to unwitting violations of the statelessness requirement, as well as other thread-safety hazards.
...
Many computations where one might be tempted to use side-effects can be more safely and efficiently expressed without side-effects, such as using reduction instead of mutable accumulators. However, side-effects such as using println()
for debugging purposes are usually harmless. A small number of stream operations, such as forEach()
and peek()
, can operate only via side-effects; these should be used with care.
With streams it's not always possible to terminate the execution early (that's a downside of relinquishing the total control over the iterations process). For instance, collect()
operation lacks short-circuiting.
To summarize, both loops and streams have their benefits and limitations. Your job is to pick the right tool for the problem at hand. Some problems are easier to implement using imperative logic.
Now, regarding the question of refactoring the for
-loop you shared into a stream.
The logic of this for
-loop can indeed be expressed as a stream correctly and without the loss of short-circuiting by using anyMatch()
operation.
IntStream.range(0, 5).anyMatch(i -> trySomething());
That's the cleanest way to achieve this (let's blind on the use of the magic number 5
for now), this stream statement is concise and bears low cognitive load.
"if there are any alternatives" - yes, but these are not better alternatives.
In a hypothetical scenario, you can come with one of them (instead of using anyMatch
). I'll show one as an illustration:
!IntStream.range(0, 5).noneMatch(i -> trySomething());
This example is an exact equivalent. Pause and observe.
Did you spot any problem, why it's not a better alternative? It's as concise as your version with anyMatch
, but it's way more difficult to comprehend (yes, conciseness in not everything). Always, try to criticize your code, question yourself if it's clear, look for convoluted logic, redundancy, etc.
Avoid magic numbers
Since this snippet is a part of retry logic in a Microservice (I assume that because you mentioned Resileance4J), then the magic number 5
should denote the maximum number of retry attempts.
It worth defining it a static
final
field to make it an explicit notion:
private static final int RETRY_THRESHOLD = 5; void tryVariousTimesSomethingWithFor() { boolean result = IntStream.range(0, RETRY_THRESHOLD) .anyMatch(i -> trySomething()); LOGGER.log("Tried with result: " + result); }
Code formatting
Dot operator .
is meant to be placed before a method call, don't leave it dangling at the end of the previous line.
Use standard indentations.
Don't skimp on white-spaces (there are some missing in the for
-loop).
If you're using IntelliJ, shortcut for reformatting Ctrl + Alt + L (note, that it's a good practice to select a certain code element, rather than reformatting the whole file).