2

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); 
4
  • also this one: stackoverflow.com/questions/22630750/… Commented Aug 12, 2020 at 14:06
  • 1
    See also How do streams stop?. Commented Aug 12, 2020 at 15:43
  • @AndrewTobilko The spliterator solution is exactly the same as mentioned in the answer here except less generic. So failing to see why this should have been re-opened Commented Aug 12, 2020 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. Commented Aug 12, 2020 at 16:07

3 Answers 3

4

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!

Sign up to request clarification or add additional context in comments.

Comments

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); 

1 Comment

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.
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

4 Comments

Thanx. The stream.iterate( I, C, N) is Java 9 and up. I am afraid I cannot use it.
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 "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"
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).

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.