我正在玩Java 8.我在将这个for循环转换为Java 8 Stream时遇到了一些麻烦。
for (int y = 0; y < 5; y ++) {
for (int x = y; x < 10; x += 2) {
System.out.println(x+y);
}
}
请帮忙!
答案 0 :(得分:12)
转换嵌套循环的规范方法是在流上使用flatMap
,例如
IntStream.range(0, 5).flatMap(i->IntStream.range(i, 10))
.forEach(System.out::println);
你的任务的棘手部分是增加2,因为它在流API中没有直接的等价物。有两种可能性:
使用IntStream.iterate(y, x->x+2)
定义起始值和增量。然后你必须通过limit
元素的数量来修改无限流:.limit((11-y)/2)
。
因此,循环的结果代码如下所示:
IntStream.range(0, 5)
.flatMap(y->IntStream.iterate(y, x->x+2).limit((11-y)/2)
.map(x -> x+y)).forEach(System.out::println);
使用IntStream.range(0, (11-y)/2)
创建所需数量的升序int
的流,然后使用.map(t->y+t*2)
对其进行修改,以使其生成所需的内部{{1}值循环。
然后,循环的结果代码如下所示:
for
答案 1 :(得分:7)
已经Holger pointed out here,这肯定是可行的。但问题是:为什么?
我怀疑XY问题,流不是解决所有问题的方法,这是一个需要记住的重要事实。
看看你的代码在做什么,并创建一个方法,做你想做的事情,尽可能多的变化,你是:
y
(考虑到你总是从0开始)x
(考虑到你总是从0开始)y
增量操作。 (假设+1不固定)x
增量操作。x
和y
进行操作。因此我提出以下建议,如果这不是你想要的,可以减少可变性,换句话说:如果有必要,你可以更加努力。
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));
}
}
}
用作:
doMyDoubleLoop(5, y -> y + 1, 10, x -> x + 2, (x, y) -> x + y, System.out::println);
正如我所说的,这可能是矫枉过正的,所以假设围绕for循环的所有内容都是固定的,它突然变得更好了:
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));
}
}
}
用作:
doMyInternalDoubleLoop((x, y) -> x + y, System.out::println);
这是一个我建议使用的模式,如果你有一个类,你有很多操作使用这个双循环,但不想复制循环,因为它应该与DRY(Don& #39; t重复自己)原则。
答案 2 :(得分:4)
我这样做的方法是在内部filter
上使用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);
我一直在使用这种模式来分割数据:例如
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)));
答案 3 :(得分:1)
我还在努力弄明白这一点。以下内容没有样式点:
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)));
x += 2
是棘手的部分,如果范围方法有一个“增量”参数,这将是微不足道的。我使用iterate
自行执行增量,但iterate
生成无限流。我使用filter
将其限制在我想要的范围内,然后放入一个任意limit
,这样它就不会溢出并开始吐出大的负数&lt; 10。