Reading and Writing |
PipedReader
andPipedWriter
(and their input and output stream counterpartsPipedInputStream
andPipedOutputStream
) implement the input and output components of a pipe. Pipes are used to channel the output from one program (or thread) into the input of another. Why is this useful?Consider a class that implements various string manipulation utilities such as sorting and reversing text. It would be nice if the output of one of these methods could be used as the input for another so that you could string a series of method calls together to perform some higher-order function. For example, you could reverse each word in a list, sort the words, and then reverse each word again to create a list of rhyming words.
Without pipe streams, the program would have to store the results somewhere (such as in a file or in memory) between each step, as shown here:
[PENDING: get figure from book]
With pipe streams, the output from one method could be piped into the next, as shown in this figure:
[PENDING: get figure from book]
Next, we investigate a program that implements what's represented by the diagram in the previous figure. This program uses
PipedReader
andPipedWriter
to connect the input and output of itsreverse
andsort
methods in order to create a list of rhyming words. Several classes make up this program. This section shows and discusses only the elements of the program that read from and write to the pipes. Follow the code links presented here to see the whole program.First, let's look at the calling sequence of the
reverse
andsort
methods from themain
method in theRhymingWords
class:The innermost call toFileReader words = new FileReader("words.txt"); Reader rhymingWords = reverse(sort(reverse(words)));reverse
takes aFileReader
opened on the filewords.txt
that contains a list of words. The return value ofreverse
is passed tosort
, whose return value is then passed to another call toreverse
.Let's look at the
reverse
method; thesort
method is similar and you will understand it once you understandreverse
.The bold statements inpublic static Reader reverse(Reader source) { BufferedReader in = new BufferedReader(source); PipedWriter pipeOut = new PipedWriter(); PipedReader pipeIn = new PipedReader(pipeOut); PrintWriter out = new PrintWriter(pipeOut); new ReverseThread(out, in).start(); return pipeIn; }reverse
create both ends of a pipe--aPipedWriter
and aPipedReader
-- and connects them by constructing thePipedReader
"on" thePipedWriter
. Whatever's written to thePipedWriter
can be read from thePipedReader
. The connection forms a pipe, as illustrated here:[PENDING: get figure from book]
reverse
starts aReverseThread
that writes its output to thePipedWriter
and then returns thePipedReader
to the caller. The caller then arranges for a sorting thread to read from it. Thesort
method is exactly the same, except that it creates and starts aSortThread
.Using Streams to Wrap Other Streams
Thereverse
method contains some other interesting code; in particular, these two statements:The first line opens aBufferedReader in = new BufferedReader(source); ... PrintWriter out = new PrintWriter(pipeOut);BufferedReader
onsource
, the argument to reverse (aReader
). This essentially "wraps"source
in aBufferedReader
. The program reads from theBufferedReader
, which in turn reads fromsource
. The program does this so that it can useBufferedReader
's convenientreadLine
method. Similarly, thePipedWriter
is wrapped in aPrintWriter
so that the program can usePrintWriter
's convenientprintln
method. You will often see streams wrapped in this way so as to combine the various features of the many streams.
Try this: Write another version of this program that uses input streams and output streams in place of readers and writers. See for the solution.
Reading and Writing |