0

How can I create a Stream that creates a number of items based on a custom generate() method?

The question is different from the one referred to. The final result is a Stream, so I could (simplistically) use a ".forach( System.out::println)".

An example would be: Stream.generate( myGenerateMethod).forEach( System.out::println);

Or a simplistic example would be:

Stream<String> overallStream = Stream.generate( () -> {
    if( generateCounter++ < 5) {
        return "String-" + generateCounter;
    }
    // close the stream
    return null; 
});
overallStream.forEach( System.out::println) ;

UPDATE and SOLUTION: referred to answers often don't give a Stream. So reopening was better.

maxGenerateCounter = 6;
StreamSupport.stream(Spliterators.spliteratorUnknownSize(new Iterator<String>() {
    int counter = 0;

    @Override
    public boolean hasNext() {
        return counter < maxGenerateCounter;
    }

    @Override
    public String next() {
        // do something
        // check if the 'end' of the Stream is reached
        counter++; // simplistically
        if( counter > maxGenerateCounter) {
            return null; // Not important answer
        }
        return "String-" + counter;
    }
}, Spliterator.IMMUTABLE), false).forEach( System.out::println);
Michael
  • 41,989
  • 11
  • 82
  • 128
tm1701
  • 7,307
  • 17
  • 79
  • 168
  • also this one: https://stackoverflow.com/questions/22630750/is-there-any-way-to-stop-a-stream-generate-from-its-lambda-closure – Andrew Tobilko Aug 12 '20 at 14:06
  • 1
    See also [How do streams stop?](https://stackoverflow.com/q/35226823/2711488). – Holger Aug 12 '20 at 15:43
  • @AndrewTobilko The spliterator solution is exactly the same as mentioned in the answer [here](https://stackoverflow.com/a/20765715/1898563) except less generic. So failing to see why this should have been re-opened – Michael Aug 12 '20 at 15:56
  • Again, thank you very much for pointing me in all kinds of directions. By over simplifying my question, it could be concluded that other answers matched rightaway. THANK YOU. – tm1701 Aug 12 '20 at 16:07

3 Answers3

2

Thank you, developers!! You inspired me in finding the solution. Many thanks!

My problem was a bit complex, and simplifying let to a over simplified question.

As we can read the many solutions, it looks like Java and Streams is fun to solve!

Experimenting with many answers, this one works. It gives a fairly easy approach of getting a STREAM that easily can be controlled. No double checking of the criteria. I liked those anyXxx( ) answers giving insight!

maxGenerateCounter = 6;
System.out.println( "Using Splitter: ");
StreamSupport.stream(Spliterators.spliteratorUnknownSize(new Iterator<String>() {
    int counter = 0;
    @Override
    public boolean hasNext() {
        // simplistic solution, see below for explanation
        return counter < maxGenerateCounter;
    }
    @Override
    public String next() {
        // executing stuff
        // providing info for 'stopping' the stream
        counter++; // for simplicity
        if( counter > maxGenerateCounter) {
           return null; // this could be any answer. It will be filtered out. 
        }
        return "String-" + counter;
    }
}, Spliterator.IMMUTABLE), false).forEach( System.out::println);

Thank you, contributors, again!

tm1701
  • 7,307
  • 17
  • 79
  • 168
1

You've answered your own question. Your snippet is exactly how you would do it. Note that Stream.generate(lambda) only works for endless streams (you can't mark that your stream has ended), hence why the javadoc of Stream.generate start with the text: "Returns an infinite sequential...".

You can then use limit to limit this. For example:

AtomicInteger counter = new AtomicInteger();
Stream<String> stream = Stream
    .generate(() -> "String-" + count.getAndIncrement())
    .limit(5)
    ;

Note that takeWhile can be useful so that your limiter can itself also be a lambda, e.g:


AtomicInteger counter = new AtomicInteger();
Stream<String> stream = Stream
    .generate(() -> "String-" + count.getAndIncrement())
    .takeWhile(count.get() < 5)
    ;

but takeWhile isn't in 8 (it is in 11 and up).

Another other alternative is to make your own spliterator but that's rather involved.

A third alternative is to make a custom collection and rely on its iteration/stream abilities:

class StringGenerator extends AbstractList<String> {
    private final int size;
    public StringGenerator(int size) { this.size = size; }

    public int size() { return size; }
    public String get(int idx) { return "String-" + idx; }
}

...

new StringGenerator(5).stream().forEach(System.out::println);
rzwitserloot
  • 85,357
  • 5
  • 51
  • 72
  • 2
    `Stream.generate()` creates an *unordered* stream which has consequences regarding what you can expect from `limit(…)` or `takeWhile`. While the constructs shown in this answer usually provide the intended result for sequential streams, the can lead to big surprises with parallel streams. – Holger Aug 12 '20 at 15:20
1

The more functional way to write this is:

IntStream.iterate(0, i -> i < 5, i -> i + 1)
    .mapToObj(i -> "String-" + i)
    .forEach(System.out::println);

Start at zero, keep producing elements while i < 5. For each step, add 1: i -> i + 1

Michael
  • 41,989
  • 11
  • 82
  • 128
  • Thanx. The stream.iterate( I, C, N) is Java 9 and up. I am afraid I cannot use it. – tm1701 Aug 12 '20 at 14:01
  • 1
    `IntStream.range(0, 5) .mapToObj(i -> "String-" + i) .forEach(System.out::println);` Doesn’t need Java 9 and is way more efficient than `iterate`. – Holger Aug 12 '20 at 15:47
  • @Holger "*The problem is, I don't know how many items there will be. So the limit is very uncertain. For simplicity I wrote I need 5 items*" – Michael Aug 12 '20 at 15:54
  • 1
    There’s no problem if you want to get an uncertain result, e.g. `IntStream.range(0, ThreadLocalRandom.current().nextInt(20)) …`. Seriously, there’s no point in trying to guess the meaning of a now-deleted comment hinting at the existence of another requirement (that the predicate implemented via lambda expression might also be unable to fulfill). Either, the questioner says clearly what is needed or has to live with answers addressing only what has been said (and in this regard, the question should have been kept closed, as the duplicates have the same answers as this one). – Holger Aug 13 '20 at 05:32