Filter Streams Thursday, Dec 11 2008 

Overview

To implement filter streams use the FilterInputStream.java and FilterOutputStream classes. Subclasses include:

  1. DataInputStream
  2. DataOutputStream
  3. BufferedInputStream
  4. BufferedOutputStream
  5. LineNumberInputStream.java
  6. PushbackInputStream.java
  7. PrintStream

You can also use the java.io.FilterReader class, which contains only one subclass: PushbackReader.

To use a filter input or output stream, attach the filter stream to another input or output stream. For example:

BufferedReader d = new BufferedReader(new DataInputStream.java(System.in));
String input;
 
while ((input = d.readLine()) != null)
{
    ... //do something interesting here
}

(more…)

Advertisements

Pipe Streams Thursday, Dec 11 2008 

             Pipes are used to channel the output from one thread into the input of another. PipedReader, PipedWriter, PipedInputStream and PipedOutputStream implement the input and output components of a pipe.

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 a 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. With pipe streams, the output from one method could be piped into the next.

RhymingWords uses PipedReader and PipedWriter to connect the input and output of its reverse and sort methods to create a list of rhyming words. Several classes make up this program.

First, let’s look at the calling sequence of the reverse and sort methods from the main method:

FileReader words = new FileReader("words.txt");
Reader rhymingWords = reverse(sort(reverse(words)));

The innermost call to reverse takes a FileReader, which is opened on the file words.txt which contains a list of words. The return value of reverse is passed to sort, whose return value is then passed to another call to reverse.

Let’s look at the reverse method; the sort method is similar and you will understand it once you understand reverse.

public static Reader reverse(Reader src) throws IOException
{
    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;
}

PipedWriter and PipedReader are connected by constructing the PipedReader “on” the PipedWriter. Whatever is written to the PipedWriter can be read from the PipedReader. The connection forms a pipe.

The reverse method starts a ReverseThread that writes its output to the PipedWriter and then returns the PipedReader to the caller. The caller then arranges for a sorting thread to read from it. The sort method is exactly the same, except that it creates and starts a SortThread

 

Wrap a Stream

The reverse method contains some other interesting code; in particular, these two statements:

BufferedReader in = new BufferedReader(source);
...
PrintWriter out = new PrintWriter(pipeOut);

The code opens a BufferedReader on source, which is another reader of a different type. This essentially “wraps” source in a BufferedReader. The program reads from the BufferedReader, which in turn reads from source. The program does this so that it can use BufferedReader’s convenient readLine method. Similarly, the PipedWriter is wrapped in a PrintWriter so that the program can use PrintWriter’s convenient println method. You will often see streams wrapped in this way so as to combine the various features of the many streams.