将同一列表的嵌套for循环改写为Java Stream实现
用Java Stream替代嵌套循环生成两个过剩数的和集合
你之前的尝试出错是因为逻辑方向完全不对——你的代码只是筛选出存在至少一个j使得i+j不超过最大值的过剩数i,然后把这些i收集到Set里,这和你要的“所有两个过剩数之和”的目标完全偏离了。我们需要的是生成所有符合条件的i+j组合,而不是筛选i本身。
正确的Stream实现方式
我们可以用flatMap来模拟嵌套循环的逻辑:对于每个过剩数i,生成所有满足i+j <= EULER23_MAX的j(和你原循环一样,从i开始遍历j,避免重复计算a+b和b+a这类等价组合),然后把每个i+j的结果平铺成一个单一流,最后收集到Set里去重。
代码如下:
private Set<Integer> calcSumsOfTwoAbundantsAlt(List<Integer> abundants) { int max = EULER23_MAX; return abundants.stream() .flatMap(i -> abundants.stream() .filter(j -> j >= i) // 和原循环j=i的逻辑一致,避免重复组合 .map(j -> i + j) .filter(sum -> sum <= max)) .collect(Collectors.toSet()); }
代码细节解释
- flatMap的核心作用:如果用普通
map,会得到Stream<Stream<Integer>>(每个i对应一个j的sum流),而flatMap能把这些嵌套流压平成一个连续的Stream<Integer>,这正是我们需要的所有和的集合。 - j >= i的过滤:和你原循环的
j = i起始条件保持一致,利用加法交换律减少不必要的计算,避免生成重复的和。 - sum <= max的过滤:确保只收集不超过题目上限的和,和原逻辑完全对齐。
进阶优化(Java 9+)
因为你的过剩数列表是通过IntStream.range生成的,本身是升序排列的,我们可以用takeWhile提前截断流,一旦i+j超过最大值就停止遍历当前i对应的j,进一步提升效率:
private Set<Integer> calcSumsOfTwoAbundantsAlt(List<Integer> abundants) { int max = EULER23_MAX; return abundants.stream() .flatMap(i -> abundants.stream() .filter(j -> j >= i) .takeWhile(j -> i + j <= max) // 一旦sum超过上限就停止遍历 .map(j -> i + j)) .collect(Collectors.toSet()); }
这样既完全复刻了原嵌套循环的逻辑,又用Stream语法完成了你的练习需求。
内容的提问来源于stack exchange,提问作者λ Jonas Gorauskas
相关产品推荐
相关产品推荐

