4

I'm generating, let's say, the following range:

IntStream.iterate(1, i -> 3*i)

How do I limit the stream to a specific element value e.g. 100 (not elements count with limit())?

Thank you!

UPDATE the function can be arbitrary

Dmitry Senkovich
  • 5,521
  • 8
  • 37
  • 74

4 Answers4

11

If you can’t use Java 9 yet, you can use the following reimplementation of the three-arg IntStream.iterate:

public static IntStream iterate(int seed, IntPredicate hasNext, IntUnaryOperator next) {
    Objects.requireNonNull(next); Objects.requireNonNull(hasNext);
    return StreamSupport.intStream(
        new Spliterators.AbstractIntSpliterator(
            Long.MAX_VALUE, Spliterator.ORDERED|Spliterator.NONNULL) {
        private IntUnaryOperator op = i -> { op = next; return i; };
        private int value = seed;

        @Override
        public boolean tryAdvance(IntConsumer action) {
            Objects.requireNonNull(action);
            if(op == null) return false;
            int t = op.applyAsInt(value);
            if(!hasNext.test(t)) { op = null; return false; }
            action.accept(value = t);
            return true;
        }

        @Override
        public void forEachRemaining(IntConsumer action) {
            Objects.requireNonNull(action);
            IntUnaryOperator first = op;
            if(first == null) return;
            op = null;
            for(int t = first.applyAsInt(value); hasNext.test(t); t = next.applyAsInt(t))
                action.accept(t);
        }
    }, false);
}

It works similar to Java 9’s IntStream.iterate, except that you have to change the class you’re invoking the static method on (or adapt the import static statement):

iterate(1, i -> i < 100, i -> i*3).forEach(System.out::println);
1
3
9
27
81
Holger
  • 285,553
  • 42
  • 434
  • 765
5

A new method

Stream<T> iterate​(T seed,Predicate<? super T> hasNext,UnaryOperator<T> next)

was introduced in Java-9. So starting with that version it is possible to do something like this:

IntStream.iterate(1, i -> i < 100, i -> 3*i)

Which will produce 1 3 9 27 81

Anton Balaniuc
  • 10,889
  • 1
  • 35
  • 53
  • ok, seems to be the thing I'm looking for. But what about Java 8? – Dmitry Senkovich Jun 05 '18 at 13:20
  • @DmitrySenkovich: Java Stream API has been introduced in Java 8. – Nikolas Charalambidis Jun 05 '18 at 13:21
  • @Nikolas I mean this predicate `hasNext` – Dmitry Senkovich Jun 05 '18 at 13:21
  • @DmitrySenkovich, the overloaded iterate method was added in Java-9 – Anton Balaniuc Jun 05 '18 at 13:22
  • @Aominè, this will produce only `1 3 9 27 81` – Anton Balaniuc Jun 05 '18 at 13:30
  • @AntonBalaniuc , Dmitry Senkovich Right I get the question now. – Ousmane D. Jun 05 '18 at 13:31
  • @Aominè might be just me, but whenever I end-up choosing `Stream#iterate` from java-9, probably time to write that as a loop, unless there are other operations that I would need like `filter, map` etc... – Eugene Jun 05 '18 at 13:57
  • @Eugene, I agree. Developers often use `Stream#iterate` to iterate over an array or/and do some index manipulation. But `steam` isn't designed for this purpose. It can be done easily with a loop – Anton Balaniuc Jun 05 '18 at 16:15
  • @Eugene Agreed! indeed. – Ousmane D. Jun 05 '18 at 22:10
  • @AntonBalaniuc there is nothing wrong with using for example `IntStream.iterate(0, i -> i < 100, i -> i + 1)` to index into an array or a list and do something with it, but in this case it would be _better_ to use `IntStream.range(0, 100)`. Nevertheless, I also agree that using an imperative loop for the aforementioned example would probably be better. – Ousmane D. Jun 05 '18 at 22:12
  • @Aominè depends on the operation. Even an operation as simple as `toArray()` may justify using a stream, as it eliminates the need for an allocation statement with a predicted size, as well as dealing with array indices. – Holger Jun 06 '18 at 07:31
5

As addition to other answers, if you can use already, there is another possibility using Stream#takeWhile taking a Predicate as parameter.

Tested in

jshell> IntStream.iterate(1, i -> 3 * i).takeWhile(i -> i < 100).toArray();
$3 ==> int[5] { 1, 3, 9, 27, 81 }
Yassin Hajaj
  • 21,337
  • 9
  • 51
  • 89
0
IntStream.range(0, N).forEach(this::doSomething);


int[] arr = IntStream.range(start, end).toArray();
surendrapanday
  • 770
  • 2
  • 9
  • 24
  • the example was not about incrementing with 3 itself, there could be a random function. I need to limit the value – Dmitry Senkovich Jun 05 '18 at 13:19
  • maybe I'm being too stupid, sorry:) But I don't understand you: the problem is that I don't know a limit (N in your case) but I have a function and an initial value – Dmitry Senkovich Jun 05 '18 at 13:26