Lesson 3 | Efficient 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:
Creating Efficient input using Streams
Question: How do you create "efficient input" using classic "input and ouput" using Java Streams from Java 1.1?
Read() variants
Here are two variants on the read() method:
Method Signature
public int read(byte b[]) throws IOException
public int read(byte b[], int offset, int length)
throws IOException
- The first variant tries to read enough data to fill the array b.
- The second variant tries to read length bytes of data into the array b starting at position offset.
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.
Both variants of the `read()` method:
- public int read(byte b[]) throws IOException
- public int read(byte b[], int offset, int length) throws IOException
are still valid and available in
Java 21.
These methods belong to the
InputStream class and are part of Java's core I/O functionality, which has been present since Java 1.1. They are used for reading data from an input stream into a byte array:
- The first method, `read(byte b[])`, reads data into the entire byte array `b`.
- The second method, `read(byte b[], int offset, int length)`, reads data into a specific portion of the array, starting at the `offset` and reading up to `length` bytes.
Both are standard parts of Java's input/output stream handling and have not been deprecated in Java 21.
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.
Getting as many bytes as you want
Reads do not always succeed in getting as many bytes as you want.
The following code loops repeatedly until it either fills the array or sees the end-of-stream:
try{
byte[] b = new byte[100];
int offset = 0;
while (offset < b.length) {
int bytesRead = System.in.read(b, offset, b.length - offset);
if (bytesRead == -1)
break; // end-of-stream
offset += bytesRead;
} // end -while
}// end -try
catch (IOException e) {
System.err.println("Couldn't read from System.in!");
}
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.
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.
Modern Java
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.
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