I was wondering when I created my own infinite stream with Stream.generate
how the Streams which are in the standard library stop...
For example when you have a list with records:
List<Record> records = getListWithRecords();
records.stream().forEach(/* do something */);
The stream won't be infinite and running forever, but it will stop when all items in the list are traversed. But how does that work? The same functionality applies for the stream created by Files.lines(path)
(source: http://www.mkyong.com/java8/java-8-stream-read-a-file-line-by-line/).
And a second question, how can a stream created with Stream.generate
be stopped in the same manner then?
Tap the stream manager and then tap the red End Stream button to end the stream.
We can create an infinite stream of any custom type elements by passing a function of a Supplier interface to a generate() method on a Stream.
Two common reasons for buffering are 1) your internet connection is too slow to stream a video in real time, and 2) the speed at which your router sends the video to all your internet-connected devices is too slow.
Finite streams simply aren’t created via Stream.generate
.
The standard way of implementing a stream, is to implement a Spliterator
, sometimes using the Iterator
detour. In either case, the implementation has a way to report an end, e.g. when Spliterator.tryAdvance
returns false
or its forEachRemaining
method just returns, or in case of an Iterator
source, when hasNext()
returns false
.
A Spliterator
may even report the expected number of elements before the processing begins.
Streams, created via one of the factory methods inside the Stream
interface, like Stream.generate
may be implemented either, by a Spliterator
as well or using internal features of the stream implementation, but regardless of how they are implemented, you don’t get hands on this implementation to change their behavior, so the only way to make such a stream finite, is to chain a limit
operation to the stream.
If you want to create a non-empty finite stream that is not backed by an array or collection and none of the existing stream sources fits, you have to implement your own Spliterator
and create a stream out of it. As told above, you can use an existing method to create a Spliterator
out of an Iterator
, but you should resists the temptation to use an Iterator
just because it’s familiar. A Spliterator
is not hard to implement:
/** like {@code Stream.generate}, but with an intrinsic limit */
static <T> Stream<T> generate(Supplier<T> s, long count) {
return StreamSupport.stream(
new Spliterators.AbstractSpliterator<T>(count, Spliterator.SIZED) {
long remaining=count;
public boolean tryAdvance(Consumer<? super T> action) {
if(remaining<=0) return false;
remaining--;
action.accept(s.get());
return true;
}
}, false);
}
From this starting point, you can add overrides for the default
methods of the Spliterator
interface, weighting development expense and potential performance improvements, e.g.
static <T> Stream<T> generate(Supplier<T> s, long count) {
return StreamSupport.stream(
new Spliterators.AbstractSpliterator<T>(count, Spliterator.SIZED) {
long remaining=count;
public boolean tryAdvance(Consumer<? super T> action) {
if(remaining<=0) return false;
remaining--;
action.accept(s.get());
return true;
}
/** May improve the performance of most non-short-circuiting operations */
@Override
public void forEachRemaining(Consumer<? super T> action) {
long toGo=remaining;
remaining=0;
for(; toGo>0; toGo--) action.accept(s.get());
}
}, false);
}
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