Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Porting nested for loop over the same list to a Java Stream

In an effort to learn Java, I'm working on a solution to the Project Euler's problem 23 where I need to find the sum of all the positive integers which cannot be written as the sum of two abundant numbers. My solution uses Java 8 streams. I will not spoil it by posting the actual answer here, but I will discuss my strategy to get to the solution.

First, I create a list of abundant numbers using an IntStream:

List<Integer> abundants = IntStream.range(1, EULER23_MAX)
        .filter(i -> Util.sumOfDivisors(i) > i)
        .boxed()
        .collect(Collectors.toList());

Then, based on the list, I create a set of sums of 2 abundants numbers lower than the max:

private Set<Integer> calcSumsOfTwoAbundants(List<Integer> abundants) {
    Set<Integer> iset = new HashSet<>();
    Integer[] arr = abundants.toArray(new Integer[abundants.size()]);

    for (int i = 0; i < arr.length - 2; i++) {
        for (int j = i; j < arr.length - 1; j++) {
            int sum = arr[i] + arr[j];
            if (sum <= EULER23_MAX) {
                iset.add(sum);
            }
        }
    }

    return iset;
}

Lastly, I generate another stream that filters out all numbers lower than the max that are present in the set of sums of two abundants, and I sum that to get to the result.

result = IntStream.range(1, EULER23_MAX)
        .filter(x -> !sumsOfTwoAbundants.contains(x))
        .sum();

My questions is this: How can I encode the logic in calcSumsOfTwoAbundants to use a streams fluent syntax instead of the nested for loops? I have tried a couple of different things, but I keep getting to the "stream has already been closed" error message or to the entirely wrong result. I also understand the the nested for loops are perhaps faster than using streams, but this is purely an intellectual exercise ... this is what I have right now:

// wrong results
private Set<Integer> calcSumsOfTwoAbundantsAlt(List<Integer> abundants) {
    return abundants.stream()
            .filter(i -> abundants.stream()
                    .anyMatch(j -> (i + j) <= EULER23_MAX))
            .collect(Collectors.toSet());
}
like image 351
λ Jonas Gorauskas Avatar asked Jan 18 '26 15:01

λ Jonas Gorauskas


1 Answers

The most direct equivalent would be replacing each for loop with IntStream.range and nesting them with flatMap:

Set<Integer> iset = IntStream.range(0, arr.length - 2)
        .flatMap(i -> IntStream.range(i, arr.length - 1)
                .map(j -> arr[i] + arr[j]).filter(s -> s <= EULER23_MAX))
        .boxed().collect(Collectors.toSet());
like image 55
Sean Van Gorder Avatar answered Jan 21 '26 06:01

Sean Van Gorder



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!