I was playing around with Java 8. I had some trouble converting this for loop into Java 8 Stream.
for (int y = 0; y < 5; y ++) {
for (int x = y; x < 10; x += 2) {
System.out.println(x+y);
}
}
Please help!
The short version basically is, if you have a small list; for loops perform better, if you have a huge list; a parallel stream will perform better. And since parallel streams have quite a bit of overhead, it is not advised to use these unless you are sure it is worth the overhead.
Remember that loops use an imperative style and Streams a declarative style, so Streams are likely to be much easier to maintain. If you have a small list, loops perform better. If you have a huge list, a parallel stream will perform better.
Yes, streams are sometimes slower than loops, but they can also be equally fast; it depends on the circumstances. The point to take home is that sequential streams are no faster than loops.
Java 8 offers the possibility to create streams out of three primitive types: int, long and double. As Stream<T> is a generic interface, and there is no way to use primitives as a type parameter with generics, three new special interfaces were created: IntStream, LongStream, DoubleStream.
The canonical way of converting nested loops is to use flatMap
on a stream, e.g.
IntStream.range(0, 5).flatMap(i->IntStream.range(i, 10))
.forEach(System.out::println);
The tricky part on your task is the increment by two as this has no direct equivalent in the stream API. There are two possibilities:
Use IntStream.iterate(y, x->x+2)
to define start value and increment. Then you have to modify the infinite stream by limit
ing the number of elements: .limit((11-y)/2)
.
So the resulting code for your loop would look like:
IntStream.range(0, 5)
.flatMap(y->IntStream.iterate(y, x->x+2).limit((11-y)/2)
.map(x -> x+y)).forEach(System.out::println);
Use IntStream.range(0, (11-y)/2)
to create an stream of the desired number of ascending int
s and modify it with .map(t->y+t*2)
to have it produce the desired values of your inner for
loop.
Then, the resulting code for your loop would look like:
IntStream.range(0, 5)
.flatMap(y->IntStream.range(0, (11-y)/2).map(t->y+t*2).map(x -> x+y))
.forEach(System.out::println);
As Holger pointed out here already, it is certainly doable. The question though is: Why?
I am suspecting an XY problem, streams are not the solution to everything, that is an important fact to remember.
Take a look at what your code is doing, and create a method that does what you want to do, with as much variability as possible, you are:
y
maximum (considering you always start at 0)x
maximum (considering you always start at 0)y
increment operation. (Assuming the +1 is not fixed)x
increment operation.x
and y
.Hence I propose the following, variability may be reduced if this is not exactly what you want, in other words: You can hardcore more if neccessary.
private void doMyDoubleLoop(
final int yEnd, final IntUnaryOperator yIncrementOperator,
final int xEnd, final IntUnaryOperator xIncrementOperator,
final IntBinaryOperator combiner, final IntConsumer consumer
) {
for (int y = 0; y < yEnd; y = yIncrementOperator.applyAsInt(y)) {
for (int x = y; x < xEnd; x = xIncrementOperator.applyAsInt(x)) {
consumer.accept(combiner.applyAsInt(x, y));
}
}
}
Used as:
doMyDoubleLoop(5, y -> y + 1, 10, x -> x + 2, (x, y) -> x + y, System.out::println);
As I said, this might be overkill, so assuming everything revolving around the for-loops is fixed, it suddenly is a lot nicer:
private void doMyInternalDoubleLoop(final IntBinaryOperator combiner, final IntConsumer consumer) {
for (int y = 0; y < 5; y++) {
for (int x = y; x < 10; x += 2) {
consumer.accept(combiner.applyAsInt(x, y));
}
}
}
Used as:
doMyInternalDoubleLoop((x, y) -> x + y, System.out::println);
This is a pattern that I would suggest to use if you have a class on which you have operations that use this double-loop a lot, but do not want to copy around the loop, as it should be with DRY (Don't Repeat Yourself) principle.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With