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.
To generate a stream by alternating adding 2 or 3, it can be observed that every other value will be a multiple of 5. Therefore if the previous value is divisible by 5 with no remainder, we should add 2, otherwise we should add 3.
IntStream.iterate(0, i -> (i % 5 == 0) ? (i + 2) : (i + 3)).limit(10)
For two alternating integers, there will be cases where this approach is not possible. If one of the numbers is a factor of the other, that won't be possible. 2 and 4 for example.
For cases like those, you can use a more general approach and maintain a boolean outside the iterator.
It doesn't follow the functional style because your function has a side effect but hey, Java isn't a functional language. It's intuitive enough.
AtomicBoolean isTwo = new AtomicBoolean(true);
IntStream.iterate(0, i -> isTwo.getAndSet(!isTwo.get()) ? (i + 2) : (i + 4))
For 3 alternating values, in the general case you can do a similar thing to the boolean, but with an integer counter that cycles between 0, 1 and 2.
AtomicInteger counter = new AtomicInteger(0);
IntStream.iterate(0, i -> {
int count = counter.getAndUpdate(cnt -> (cnt + 1) % 3);
if (count == 0) return i + 2;
if (count == 1) return i + 5;
if (count == 2) return i + 7;
// As long as modulus value == number of if-conditions, this isn't possible
throw new RuntimeException("Only 3 possible values");
})
.limit(10)
You could use an AtomicInteger
to achieve this and keep of list of numbers that are to be added in an alternating manner.
List<Integer> adds = List.of(2,3,7);
AtomicInteger x = new AtomicInteger(0);
int limit = 10;
return IntStream.iterate(1,
i -> i + adds.get(x.getAndIncrement() % adds.size()))
.limit(limit)
.boxed()
.collect(Collectors.toList());
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With