zhengxiaoyao0716
zhengxiaoyao0716

Reputation: 13

Generate stream delayed from list

As indicated in the title, I want to generate stream from list, but the list need a large performance cost, and the stream may not be really used.

Something I need is like this:

Stream<T> stream = Stream.fromList(() -> calculateList(...))
    .filter(...)
    .map(...)
    // TODO
    ;

//#region some case why I want delayed stream instead of other way.

if (condition$1) {
    // that is why I don't want to re-order those code.
    stream.forEach(...);
    return;
}

var something$1 = ...; // that should only execute after the condition$1 not passed.

if (condition$2) {
    // that is why I don't want to use `Supplier<Stream<T>>`.
    stream = stream.concat(Stream.fromList(() -> calculateOthList(...)));
}

var something$2 = ...;

if (condition$3) {
    // that is why I dont want to use `Stream<Supplier<Stream<T>>>` (and what's more, it not easy to read).
    stream = stream.filter(...).peek(...);
}

... // as you see, the most important thing of those code is to deal the stream, it's data-driven.
//#endregion

// TODO may be return if some condition not passed.
// TODO and even worse, some exception may be throw.

stream.forEach(...); // TODO really use the stream.


Is there any built-in methods to do that? or maybe any way to implement elegantly?

Upvotes: 0

Views: 512

Answers (4)

Sleiman Jneidi
Sleiman Jneidi

Reputation: 23339

You can create a Spiliterator from a Supplier and get a Stream back which makes your Stream creation lazy. For example:

 Supplier<Spliterator<Integer>> supplier = () -> calculateList(...).spliterator();
 Stream<Integer> stream = StreamSupport.stream(supplier, Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED, false);

Upvotes: 2

WJS
WJS

Reputation: 40047

You could do it like this.

        List<Integer> list = List.of(1,2,3,4,5,6);
        Function<List<Integer>,Stream<Integer>> fnc = a->a.stream().filter(b->b%2 == 0).map(b->b*20);


        fnc.apply(list).forEach(System.out::println);

In your case it could be

       fnc.apply(calculateList(...)).forEach(....);

Upvotes: 0

Ashley Frieze
Ashley Frieze

Reputation: 5458

In this situation you really need to provide a Supplier. The Supplier will evaluate your list populating method when you want it. There are two ways to achieve this:

   Supplier<T> streamSupplier = () -> calculateList(...).stream();
   // other code
   // then use it
   streamSupplier.get().forEach(...);

Or maybe you really want a stream, so might do something like this:

   Stream<Supplier<Stream<T>> streamOfStreamSuppliers = Stream.of(() -> calculateList().stream());
   // other code
   // then use it by unpacking the supplied stream
   // and then flatmapping that
   streamOfStreamSuppliers.map(Supplier::get)
       .flatMap(Function.identity())
       .forEach(...);

This has the side effect of allowing you to put multiple streams into the mix.

The bigger question, though, is why you don't reorganise your code so that your stream is created when it's first needed?

Upvotes: 0

MikeFHay
MikeFHay

Reputation: 9043

Probably the simplest thing is to replace your Stream with a Supplier<Stream> e.g.

Supplier<Stream<T>> stream = () -> Stream.fromList(() -> calculateList(...));

but if you can't do that, you can implement a lazy Stream like this:

Stream<T> lazyStream = Stream.<Supplier<List<T>>>of(() -> calculateList())
            .map(Supplier::get)
            .flatMap(List::stream);

Upvotes: 0

Related Questions