<T> Stream.fromForEach(Consumer<Consumer<T>>)

Remi Forax forax at univ-mlv.fr
Sat May 21 22:54:07 UTC 2022


Hi all,
a stream is kind of push iterator so it can be created from any object that has a method like forEach(Consumer),
but sadly there is no static method to create a Stream from a Consumer of Consumer so people usually miss that creating a Stream from events pushed to a consumer is easy.

By example, let say i've a very simple parser like this, it calls the listener/callback for each tokens 

  record Parser(String text) {
    void parse(Consumer<? super String> listener) {
      for(var token: text.split(" ")) {
         listener.accept(token);
      }
    }
  } 

Using the method Stream.fromForEach, we can create a Stream from the sequence of tokens that are pushed through the listener
  var parser = new Parser("1 2");
  var stream = Stream.fromForEach(parser::parse);

It can also works with an iterable, an optional or even a collection
  Iterable<String> iterable = ...
  var stream = Stream.fromForEach(iterable::forEachRemaning);

  Optional<String> optional = ...
  var stream = Stream.fromForEach(optional::ifPresent);

  List<String> list = ...
  var stream = Stream.fromForEach(list::forEach);

I known the last two examples already have their own method stream(), it's just to explain how Stream.fromForEach is supposed to work.

In term of implementation, Stream.fromForEach() is equivalent to creating a stream using a mapMulti(), so it can be implemented like this

  static <T> Stream<T> fromForEach(Consumer<? super Consumer<T>> forEach) {
    return Stream.of((T) null).mapMulti((__, consumer) -> forEach.accept(consumer));
  }

but i think that Stream.fromForEach(iterable::forEachRemaning) is more readable than Stream.of(iterable).mapMult(Iterable::forEachRemaining).

The name fromForEach is not great and i'm sure someone will come with a better one.

regards,
Rémi


More information about the core-libs-dev mailing list