Win a copy of Kotlin in Action this week in the Kotlin forum!
  • Post Reply Bookmark Topic Watch Topic
  • New Topic

Creating a Stream<String> reading from System.in?  RSS feed

 
Campbell Ritchie
Marshal
Posts: 55698
163
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I was wondering whether it is possible to create a Stream reading from System.in. Once I had read a few methods in the Stream interface, I thought it looked easy enough.
Stream.generate(new Scanner(System.in)::nextLine).etc();
That seems to work. Is there a neater way to do it? If you try to close the Stream by feeding ctrl‑D or ctrl‑Z (on a Linux box) it seems to produce a NoSuchElementException but that is normal for a Scanner if you close its input. It would appear to be an infinite Stream (or would be if I had an ∞ number of fingers to work the keyboard and produce input).
 
Stephan van Hulst
Saloon Keeper
Posts: 7806
142
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
 
Campbell Ritchie
Marshal
Posts: 55698
163
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Thank you.
 
Stephan van Hulst
Saloon Keeper
Posts: 7806
142
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Sadly it doesn't let you transform the input to forms larger than single lines, but at least it's something
 
Campbell Ritchie
Marshal
Posts: 55698
163
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Don't you have a joining method? Or is that a terminal operation?

Which of the two is better?
 
Stephan van Hulst
Saloon Keeper
Posts: 7806
142
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
You mean your original code versus mine? I would say that if you want to operate on individual lines, my example conveys the intention more clearly. Your code is more flexible though, as it allows you to tokenize the input in any way you want (consider Scanner::findWithinHorizon). I'm not sure if that's really helpful for keyboard input though.

I don't really like using generate() without a limit() or find...() operation, because generate() and iterate() are supposed to return infinite streams. If I'm honest, I don't like using streams for input in the first place, because lazy evaluation is unreliable if the underlying data source is not referentially transparent. Another problem with generate() is that it produces an unordered stream, which means that if the user enters multiple lines, operations performed on late user input may be executed before operations on early user input.

I feel Streams in Java are like inheritance and operator overloading: Awesome features, but when we first learn about them, we tend to overuse them. My advice is to stick to imperative programming when you have a 'sequential' flow in your application, like handling user input. Streams are awesome for processing *real* collections.
 
Campbell Ritchie
Marshal
Posts: 55698
163
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I hadn't noticed the bit about unordered. Agree that a loop with while (myScanner.hasNextXXX())... gets you out of the no such element Exception. Shall try later with the Buffered Reader and lines(). If the Stream fro generate is unordered, you probably won't notice that because all computation done by the Stream will be faster than the keyboard, and you will finish it before the next line of input is available.
 
Campbell Ritchie
Marshal
Posts: 55698
163
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
The lines() version has the advantage that there is no Exception thrown when the input is closed with ctrl‑D.
 
It is sorta covered in the JavaRanch Style Guide.
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!