20

How do you iterate through a range of numbers (0-100) in steps(3) with IntStream?

I tried iterate, but this never stops executing.

IntStream.iterate(0, n -> n + 3).filter(x -> x > 0 && x < 100).forEach(System.out::println)
JasperJ
  • 1,192
  • 1
  • 12
  • 23
  • didn't understand why you ask and answer in the same time ?!? – Yagami Light Nov 01 '16 at 11:07
  • @YagamiLight I couldn't find this answer on stackoverflow. Just want to help others and expand the knowledge base. And maybe others have a higher performing solution, since my answer is not 'ideal'. – JasperJ Nov 01 '16 at 11:09
  • 2
    i totally understand and really like this why of thinking but you could ask to improve your answer (by adding it in the question) or simplly wait a littble bit longer – Yagami Light Nov 01 '16 at 11:13
  • The filter() method, if not otherwise limited, will search through a whole collection (which in your case is effectively infinite). What you're looking for is a takeWhile() method (that iterates only up to a first element that falsifies a predicate). Look for example here: https://stackoverflow.com/questions/20746429/limit-a-stream-by-a-predicate – user3078523 Feb 01 '18 at 16:44

7 Answers7

20

Actually range is ideal for this.

IntStream.range(0, 100).filter(x -> x % 3 == 0); //107,566 ns/op [Average]

Edit: Holgers's solution is the fastest performing solution.

Since the following lines of code

IntStream.range(0, 100).filter(x -> x % 3 == 0).forEach((x) -> x = x + 2); 

IntStream.range(0, 100 / 3).map(x -> x * 3).forEach((x) -> x = x + 2); 

int limit = ( 100 / 3 ) + 1; 
IntStream.iterate(0, n -> n + 3).limit(limit).forEach((x) -> x = x + 2);

show these benchmark results

Benchmark                 Mode  Cnt    Score    Error  Units
Benchmark.intStreamTest   avgt    5  485,473 ± 58,402  ns/op
Benchmark.intStreamTest2  avgt    5  202,135 ±  7,237  ns/op
Benchmark.intStreamTest3  avgt    5  280,307 ± 41,772  ns/op
JasperJ
  • 1,192
  • 1
  • 12
  • 23
  • 11
    `IntStream.range(0, 100/3).map(i -> i*3)` might be more efficient… – Holger Nov 01 '16 at 11:16
  • @Holger Awesome, your solution performs 2,4 times higher! – JasperJ Nov 01 '16 at 13:42
  • 5
    Well, 33 multiplications *should* be faster than 100 modulo operations. But note that when benchmarking, you have to care that the JIT can’t optimize the consumer to a no-op, i.e. if you use JMH, call `BlackHole.consume` with the `x` in the consumer. – Holger Nov 01 '16 at 14:36
  • What if we need range 900 to 1900 with step 5 – gstackoverflow Feb 15 '19 at 16:39
16

Actually you can also achieve the same results with a combination of peek and allMatch:

IntStream.iterate(0, n -> n + 3).peek(n -> System.out.printf("%d,", n)).allMatch(n -> n < 100 - 3);

This prints

0,3,6,9,12,15,18,21,24,27,30,33,36,39,42,45,48,51,54,57,60,63,66,69,72,75,78,81,84,87,90,93,96,99,

But nevertheless, this one is faster:

IntStream.range(0, 100 / 3 + 1).map(x -> x * 3).forEach((x) -> System.out.printf("%d,", x));

Java 9 Update:

Now the same iteration easier to achieve with Java 9:

Stream.iterate(0, i -> i <= 100, i -> 3 + i).forEach(i -> System.out.printf("%d,", i));
gil.fernandes
  • 12,978
  • 5
  • 63
  • 76
  • how do you get rid of the last comma in the printout using the same code? – CanCoder Oct 20 '19 at 14:36
  • 2
    @leeCoder Something like: `String joined = IntStream.range(0, 100 / 3 + 1).map(x -> x * 3).mapToObj(Integer::toString).collect(Collectors.joining(","));` – gil.fernandes Oct 20 '19 at 19:33
8

In JDK9 there's takeWhile 1

IntStream
  .iterate(0, n -> n + 3)
  .takeWhile(n -> n < 100)
  .forEach(System.out::println);
davidsheldon
  • 38,365
  • 4
  • 27
  • 28
6

limit can also be used

int limit = ( 100 / 3 ) + 1;
IntStream.iterate(0, n -> n + 3).limit(limit).forEach(System.out::println);
Saravana
  • 12,647
  • 2
  • 39
  • 57
5

Elegant Solution:

IntStream.iterate(0, n -> n < 100, n -> n + 3).forEach(System.out::println)

Stream.iterate() supports a hasNext() predicate (added in Java 9) which can be used to limit the stream in more natural way.

shmosel
  • 49,289
  • 6
  • 73
  • 138
Sri
  • 4,613
  • 2
  • 39
  • 42
3

If you are ok adding a library dependency, the IntInterval class in Eclipse Collections has the step function I think you are looking for. I tried a few different approaches converting IntInterval to an IntStream, since the original question asked for IntStream. Here are the solutions I came up with using IntInterval and then converting it to an IntStream.

IntInterval interval = IntInterval.zeroTo(99).by(3);
interval.each(System.out::print);

IntStream.of(interval.toArray()).forEach(System.out::print);

IntStream.Builder builder = IntStream.builder();
interval.each(builder::add);
builder.build().forEach(System.out::print);

IntStream.generate(interval.intIterator()::next)
    .limit(interval.size()).forEach(System.out::print);

IntInterval is inclusive on the from and to like IntStream.rangeClosed().

Note: I am a committer for Eclipse Collections

Donald Raab
  • 6,458
  • 2
  • 36
  • 44
2

More generic solution:

LongStream.range(0L, (to - from) / step) // +1 depends on inclusve or exclusive
                .mapToObj(i -> (from + i * step))
                .collect(Collectors.toList());
gstackoverflow
  • 36,709
  • 117
  • 359
  • 710