Java Streams  «Prev  Next»

Lesson 3Efficient input
Objective Write a program that uses a byte array buffer to copy data from System.in to System.out.

Efficient byte array buffer

Input and output are often the performance bottleneck in a program. Reading from or writing to disk can be hundreds of times slower than reading from or writing to memory. Network connections and user input are even slower.
While disk capacities and speeds have increased over time, they have never kept pace with CPU speeds. Therefore, it's important to minimize the number of reads and writes a program actually does.
Most input stream classes have efficient polymorphicread() methods that read chunks of contiguous data into a byte array. This is faster than reading each element of the array separately.
To attempt to read 10 bytes from System.in, you could use the following code:

try {
  byte[] b = new byte[10];
  System.in.read(b);
}
catch (IOException e) {
  System.err.println("Couldn't read from System.in!");
}

Reads don't always succeed in getting as many bytes as you want<.
Conversely, there is nothing to stop you from trying to read more data into the array than will fit.
If you do read more data than the array has space for, an ArrayIndexOutOfBoundsException will be thrown.

The read() method waits or blocks until a byte of data is available and ready to be read. Input and output can be slow, so if your program is doing anything else of importance, you should try to put I/O in its own thread. read() is declared abstract; therefore, InputStream is abstract. Hence, you can never instantiate an InputStream directly and always have to work with one of its concrete subclasses.

Byte Array Buffer - Exercise

Click the Exercise link below to write a program that uses a byte array buffer, an InputStream, and the System.out.println() method to copy data from System.in to System.out.
Byte Array Buffer - Exercise

Java I/O

I/O (input/output) has been around since the beginning of Java. You could read and write files along with some other common operations. Then with Java 1.4, Java added more I/O functionality and cleverly named it NIO. That stands for "new I/O." The APIs prior to Java 7 still had a few limitations when you had to write applications that focused heavily on files and file manipulation. Trying to write a little routine listing all the files created in the past day within a directory tree would give you some headaches. There was no support for navigating directory trees, and just reading attributes of a file was also quite hard. In Java 7, this whole routine is less than 15 lines of code.
Now what to name yet another I/O API? The name "new I/O" was taken, and "new new I/O" would just sound silly. Since the Java 7 functionality was added to package names that begin with java.nio, the new name was NIO.2. For the purposes of this chapter and the exam, NIO is shorthand for NIO.2. Since NIO or NIO.2 builds upon the original I/O.
Fortunately, you will not have to become a total I/O or NIO guru to write input and output programs in Java.

Java7 NIO-2

Reading Chunks of Data from a Stream

Input and output are often the performance bottlenecks in a program. Reading from or writing to disk can be hundreds of times slower than reading from or writing to memory; network connections and user input are even slower. While disk capacities and speeds have increased over time, they have never kept pace with CPU speeds. Therefore, it's important to minimize the number of reads and writes a program actually performs.
All input streams have overloaded read() methods that read chunks of contiguous data into a byte array. The first variant tries to read enough data to fill the array data. The second variant tries to read length bytes of data starting at position offset into the array data. Neither of these methods is guaranteed to read as many bytes as they want. Both methods return the number of bytes actually read, or -1 on end of stream.
public int read(byte[] data) throws IOException
public int read(byte[] data, int offset, int length) throws IOException

The default implementation of these methods in the java.io.InputStream class merely calls the basic read() method enough times to fill the requested array or subarray. Thus, reading 10 bytes of data takes 10 times as long as reading one byte of data. However, most subclasses of InputStream override these methods with more efficient methods, perhaps native, that read the data from the underlying source as a block.