IntStream.iterate with alternating values to add



I want to create a sequence of integers, preferably as an IntStream, which follows a certain condition. some simple examples to explain what I am trying to do:

Sequence of first 10 even numbers starting from 0 (Here i have no problems, I do it with the below snippet)

IntStream.iterate(0, i -> i + 2).limit(10); 

//0,2,4,6,8,10,12,14,16,18

Sequence of first 10 numbers starting from 0 by alternately adding 2 and 3 to the privious number; desired output:

 //0,2,5,7,10,12,15,17,20,22

I would love to use for this scenario also IntStream.iterate() or IntStream.generate() but couldn’t make it on my own. I am using classic for loops, which works but is somehow long for such a relative simple task

List<Integer> list = new ArrayList<>();
list.add(0);
for(int i = 1, j = 1; i < 10; i++, j *= -1){
    if(j > 0){
        list.add(list.get(i-1) + 2);
    }
    else{
        list.add(list.get(i-1) + 3);
    }
}

//and then stream over the list to get IntStream
list.stream().mapToInt(Integer::intValue);

Any easy way to achieve the same as above with IntStream.iterate() or IntStream.generate()?

For three and more alternating values I am out of ideas how to do it elegantly. If for example I want to create the Sequence of first 10 numbers starting from 0 by alternately adding +2, +5 and +7 to the previous number with the desired output:

//0,2,7,14,16,21,28,30,35,42

I have thought about using i%3 in a for loop with 3 if-else blocks or switch cases but this will grow when i need more alternating values and i have to add more ifs or cases. Any ideas how to do it? I am open for other approaches too if you think IntStream.iterate() or IntStream.generate() is not the proper way of solving the described task.

Answer

You can look at this mathematically. Say you want to add x1, x2, x3 … xn, alternately.

The first n terms can be written as:

(0 * sum of all x's)
(0 * sum of all x's + x1)
(0 * sum of all x's + x1 + x2)
(0 * sum of all x's + x1 + x2 + x3)
...
(0 * sum of all x's + sum of all x's - xn)

The next n terms can be written the same way, except you replace all the 0s with 1s.

(1 * sum of all x's)
(1 * sum of all x's + x1)
(1 * sum of all x's + x1 + x2)
(1 * sum of all x's + x1 + x2 + x3)
...
(1 * sum of all x's + sum of all x's - xn)

And the next n terms you replace all the 1’s with 2’s, and so on.

So we just need to generate the stream (1, 2, 3, 4…) and flatMap each element i to the stream of

(i * sum of all x's)
(i * sum of all x's + x1)
(i * sum of all x's + x1 + x2)
(i * sum of all x's + x1 + x2 + x3)
...
(i * sum of all x's + sum of all x's - xn)

Using this pattern, you can write your 2, 5, 7 stream like this:

final int a = 2, 
final int b = 5, 
final int c = 7;
final int sum = a + b + c;
IntStream.iterate(0, i -> i + 1).flatMap(
    i -> IntStream.of(i * sum, i * sum + a, i * sum + a + b)
).limit(10);

If you wanted to do 2,3,5,7 instead:

final int a = 2, 
final int b = 3, 
final int c = 5; 
final int d = 7;
final int sum = a + b + c + d;
IntStream.iterate(0, i -> i + 1).flatMap(
    i -> IntStream.of(i * sum, i * sum + a, i * sum + a + b, i * sum + a + b + c)
).limit(10);

I’ll leave it to you to generalise this to any int[] of numbers.



Source: stackoverflow