11

I want to generate a list of numbers using lambda expressions and not a for-loop.

So let's say I want to generate a list of all triangular numbers under 100. Triangular numbers are numbers which follow the formula: (n*n+n)/2

What is the best way of doing this? Currently I have this:

    Stream.iterate(1, n -> n + 1).limit(100)
            .map(n -> (n * n + n) / 2)
            .filter(a -> a < 100)
            .map(a -> a + "")
            .collect(Collectors.joining(", ", "Numbers: ", "."));

But this seems unnecessarily overkill with the amount of calculations. I iterate n over 1 to 100 (because lets assume I do not know what the max value for n is), then I map the triangle number function of that list and then I check which numbers are under 100. Is there a more efficient way in doing this? Also: can I generate the triangle numbers using only the iterate function of Stream instead of using iterate, limit and then map?

EDIT: So the main point here is: how can calculation of traingle numbers stop as soon as one of the triangle numbers exceeds 100? Usually I would write it like this:

ArrayList<Integer> triangles = new ArrayList<>(); 
for (int n=1;true;n++) {
    int num = (n*n+n)/2;

    if (num>100) break;

    triangles.add(num);
}

which stops as soon as a triangle number exceeds 100, which is very efficient; how can I retain this efficiency in lambda expression?

3
  • 2
    Stream.iterate(1, n->n+1).limit(100) can be rewritten as IntStream.rangeClosed(1, 100) which is probably more readable. Commented Jun 7, 2015 at 13:22
  • why are you using both a limit and a filter? I believe the second filter will limit the output based on the calculation, so you'll only get results that are less than 100, rather than inputs less than 100. Commented Jun 7, 2015 at 13:23
  • What is the point? Or is it just curiosity? Commented Jun 7, 2015 at 13:23

2 Answers 2

6

In general case what you're looking for is take-while. Unfortunately, it has no default implementation in Java 8 streams. See a question about take-while.

Sign up to request clarification or add additional context in comments.

1 Comment

takeWhile was added in Java 9.
-2

If all you're looking to do is convert the given sequence into the triangle (as you describe), this does it much simpler.

List<Integer> l = IntStream.rangeClosed(1, 100)
            .mapToObj(n -> (n*n + n) / 2)
            .collect(Collectors.toList());

the primitive stream wrappers need an extra step to up-convert to objects, hence the mapToObj method.

If you're looking to stop filtering when you hit 100, easiest way I can think of is

    IntFunction<Integer> calc =n -> (n*n+n) / 2; 
    List<Integer> l = IntStream.rangeClosed(1, 100)
            .filter(n -> calc.apply(n) < 100)
            .mapToObj(calc)
            .collect(Collectors.toList());

Based on the changes to your question, I think this is also pretty important to point out. If you want to mirror what you used to do, that would look like this:

    List<Integer> results = new ArrayList<>(100);
    IntStream.rangeClosed(1, 100).forEach(i -> {
        int tri =calc.apply(i);
        if(tri < 100) {
            results.add(tri);
        }
    });

It's worth pointing out that streams are not necessarily ordered (though the default implementation follows the iterator). If this were converted to a parallel stream you would see the difference (and power of streams). You can't break from the execution because then you're assuming a certain amount about the processing order. By filtering early (in my second form) you'll ensure that you only end up with a result stream of 13 entries in it before the final calculation. Take this parallel option as a note as well.

    List<Integer> l = IntStream.rangeClosed(1, 100).parallel()
            .filter(n -> calc.apply(n) < 100)
            .mapToObj(calc)
            .collect(Collectors.toList());

You'll see they're still ordered, but the computation of them was done on multiple threads.

7 Comments

OP is asking how to stop streaming when value of (n*n + n) / 2 will start to become greater than 100, because of hight amount of cases which will need to be filtered. So question is "if we know that values after some n-th value will not be needed how can we skip them".
Op hasn't responded to any questions yet. It's not clear what he's asking for yet.
@JohnAment if it's not clear to you what the question is, that's a good reason not to answer.
Your solution is worse than the one of the OP: it performs the computation twice for every number leading to a result < 100: once to filter, and once to map.
@JohnAment that's not the problem here. Your code, using streams, is slower than the OP's code, also using streams, and it doesn't do what the OP wants: stop iterating and doing the computations as soon as a result >= 100 is found.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.