Imagine there is a large tank filled with water and there is small tap at the bottom through which water is coming out and this is called stream. You might not have any idea regarding the amount of water in the tank. The only thing you can work on is that stream of water coming out from the tank. Streams in computing is similar. It is a sequence of data made available over a time. Unlike data which are fixed, streams can be unlimited. We can filter it, connect it to a pipeline or redirect it as a different stream (as in an inlet stream to another tank), but we can never work on the entire tank through just the stream.
Where do streams come into picture? Answer is almost anywhere data transfer is taking place. For example, Network stream, it is actually a series of ones and zeroes transferring over a media. You never know how much of data would be coming. Reading from storage, file stream, it is series of memory locations being read from a disk. The disk would be rotating and when the head is at a required location, some kind of interrupt is being fired to read that location.
Now imagine, you have to transfer the contents from one tank to another. One way is to direct the outlet stream of the first tank as an inlet to the other. However, if for any reason, you couldn’t make this direct transfer, then how would you do this transfer ? Well, you can bring a bucket, fill it with the outlet stream from the first tank and transfer it to the second tank and then you will keep repeating the process. In this case, your bucket is the buffer. It is temporary and is of fixed size. Buffers in computing are similar. It is a region of a physical memory storage used to temporarily store data while it is being moved from one place to another.
Readers and writers
You would have these in almost all programming languages. They are an abstraction on top of streams. Since streams are just a series binary data, readers and writers would help in converting them to and from characters. While reading from a stream, a reader would convert the binary data to characters based on the encoding you specify. And a writer would similarly convert characters to binary data.
It should now be clear to you that a stream will be a series of binary data. Since you can’t read the entire data represented by a stream, you have to read it in chunks of fixed size buffer. A buffer would then be a byte array. Amount of data already read from a stream is like amount of water already filled into a bucket and therefore that data can no longer be read from the stream again. The buffer can be emptied or filled in some other location. However, the entire content must be converted to characters based upon some encoding format to make sense out of it. Once the above mentioned concepts are clear, we can start working on them.
In the following examples, we will be working on a File stream. However the concepts remain the same for all streams. Let us therefore start by creating a file (abc.txt) with the following content.
Streams in computing is a sequence of data made available over a time.
Reading a line from the file using built in readers. We will first create a FileInputStream. We will use a byte array as buffer to read 10 bytes from the stream ourselves. Then we will use a reader to read from an InputStream. So we will use InputStreamReader. Since, reading byte by byte is expensive, we will wrap it around a BufferedReader to read it in larger chunks.
// creating a file input stream FileInputStream fileInputStream = new FileInputStream("abc.txt"); // byte array as buffer to hold read data byte buffer = new byte; // reading from the stream directly fileInputStream.read(buffer); // since the conversion to characters has not been done based on encoding, // the printed buffer would not be readable System.out.println(buffer); // we can use the above method to read the entire file in memory and then // use proper encoding to convert the byte array to proper characters or // we can use reader to read from the stream so that the encoding is taken care of InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream); // array to hold the read characters from inputstream char charArray = new char; // reads only the no. of characters as per the size of the charArray inputStreamReader.read(charArray); // prints the charArray System.out.println(charArray); // computing (doesn't print first 10 chars as they were already read in the buffer) // we can use the above method to read in chunks of 10 chars until the whole file is read // or we can used the BufferedReader which will do this for us BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String firstLine = bufferedReader.readLine(); System.out.println(firstLine); // Prints the rest of the line as buffer and 10 chars have already been read
The similar concept apply when working with output streams. Note that when we use any kind of buffer to read from a stream, we need to start a loop to keep reading from the stream until the entire stream is read. Similar loop would be required while writing to a stream.