问题
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!
回答1:
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 bylimit
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 ascendingint
s and modify it with.map(t->y+t*2)
to have it produce the desired values of your innerfor
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);
回答2:
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:
- Having an
y
maximum (considering you always start at 0) - Having an
x
maximum (considering you always start at 0) - Having an
y
increment operation. (Assuming the +1 is not fixed) - Having an
x
increment operation. - Then an operation with the
x
andy
. - Then you consume the result of the operation.
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.
回答3:
The way I would do this is to use a filter
on the inner IntStream
:
IntStream
.range(0, 5)
.flatMap(
y ->
IntStream
.range(y, 10)
.filter(x -> (x-y) % 2 == 0)
.map(x -> x+y))
.forEach(System.out::println);
I've been using this pattern for segmenting data: E.g.
IntStream
.range(first, last)
.filter(index -> (index-first) % segment == 0)
.forEach(
System.out.printf(
"Doing segment %s to %s%n",
index,
Math.min(index+seg-1, last)));
回答4:
I'm still trying to figure this out myself. The following gets no points for style:
IntStream.range(0, 5).forEach(y ->
//<kludge>
//I would appreciate it if someone would replace this with something smarter.
IntStream.iterate(y, x -> x + 2)
.limit(100)
.filter(x -> x < 10)
//</kludge>
.forEach( x -> System.out.println(y+x)));
The x += 2
is the tricky part, which would have been trivial if the range method had an "increment" parameter. I used iterate
to do the increments myself, but iterate
produces an infinite stream. I used filter
to restrict it to the range I want, and put in an arbitrary limit
so that it wouldn't just overflow and start spitting out large negative numbers that are < 10.
来源:https://stackoverflow.com/questions/23688967/converting-for-loop-to-java-8-stream