Download (direct link):
org.javapitfalls.item2.SlowFileCopy j2sdk-1_4_1-beta-windows-i586.exe ^
Elapsed Time: 13971 milliseconds.
Lines 30 and 31 of Listing 2.1 are the key workhorse loop of the program where bytes are transferred from the original file to the buffer and then are written from the buffer to a second file (the copy). The FileChannel class includes a method called transferTo() that takes advantage of low-level operating system calls specifically to speed up transfers between file channels. So, the loop in 26 to 31 can be replaced by the following code:
FileChannel fcin = fis.getChannel();
FileChannel fcout = fos.getChannel();
fcin.transferTo(0, fcin.size(), fcout);
The above snippet is from the program called FastFileCopy.java, which is identical to SlowFileCopy.java except for the lines above that replace the while loop. A run of FastFileCopy on the same large file produces:
org.javapitfalls.item2.FastFileCopy j2sdk-1_4_1-beta-windows-i586.exe ^
Elapsed Time: 2343 milliseconds.
The performance of FastFileCopy is very fast for all large files, but slightly slower for smaller files.
Little-Endian Byte Operations
A nice feature of the NIO Buffer class is the ability to perform reads and writes of the numeric data types using either Big Endian or Little Endian byte order. For those not familiar with the difference, for multibyte data types like short (2 bytes), integer and float (4 bytes), and long and double (8 bytes), Little Endian stores the bytes starting from the least significant byte ("littlest" number) toward the most significant. Of course, Big Endian stores bytes in the opposite direction. Processors from Motorola and Sun use Big Endian order, while Intel uses Little Endian. Prior to JDK 1.4, you would have to perform the byte-swapping yourself. I created a class called LittleEndian-OutputStream to do just that for a BMP Image encoder. Listing 2.2 demonstrates the byte swapping. This is necessary because the only order available for DataOutput-Stream is Big Endian.
22 Item 2
008 class LittleEndianOutputStream extends OutputStream
010 OutputStream os;
012 public LittleEndianOutputStream(OutputStream os)
014 this.os = os;
017 public void write(int b) throws IOException
022 public void writeShort(short s) throws IOException
024 int is = (int) s; // promote
025 int maskB1 = 0xff;
026 int maskB2 = 0xff00;
028 byte  b = new byte;
029 b = (byte) (s & maskB1);
030 b = (byte) ((s & maskB2) >>> 8);
035 public void writeInt(int i) throws IOException
037 byte  b = new byte;
038 int maskB1 = 0xff;
039 int maskB2 = 0xff00;
040 int maskB3 = 0xff0000;
041 int maskB4 = 0xff000000;
043 b = (byte) ((i & maskB4) >>> 24);
044 b = (byte) ((i & maskB3) >>> 16);
045 b = (byte) ((i & maskB2) >>> 8);
046 b = (byte) (i & maskB1);
Listing 2.2 LittleEndianOutputStream.java
NIO Performance and Pitfalls 23
The LittleEndianOutputStream was used in combination with a DataOut-putStream to write integers and shorts in a BMP encoder. Note how we manually swap the bytes when writing an integer to the underlying OutputStream in the writeInt() method. We swap the bytes by masking the byte in the original integer (Big Endian format), shifting it down to the lowest byte position and assigning it to its new byte position. LittleEndianOutputStream is now obsolete, as Listing 2.3 shows the encoder rewritten using NIO Buffers.
001 /** BmpWriter3.java */
002 package org.javapitfalls.item2;
004 import java.awt.*;
005 import java.awt.image.*;
006 import java.io.*;
007 import java.nio.*;
008 import java.nio.channels.*;
010 public class BmpWriter3
012 // File Header - Actual contents (14 bytes):
013 short fileType = 0x4d42;// always "BM"
014 int fileSize; // size of file in bytes
015 short reserved1 = 0; // always 0
016 short reserved2 = 0; // always 0
017 int bitmapOffset = 54; // starting byte position of image data
019 // BMP Image Header - Actual conents (40 bytes):
020 int size = 40; // size of this header in bytes
021 int width; // image width in pixels
022 int height; // image height in pixels (if < 0, top-down ")
023 short planes = 1; // no. of color planes: always 1
024 short bitsPerPixel = 24;// number of bits per pixel: 1, 4, 8, 2
or 24 (no color map)
// Some data members omitted for brevity - - code available online.
038 public void storeImage(Image img, String sFilename) throws 2
// . .. getting Image width and height omitted for brevity .
057 width = imgWidth;
058 height = imgHeight;