Java : NIO

http://yamarou.at.infoseek.co.jp/javanawake/022.html
java.ioとjava.nioのファイル読み込み速度を比較しているのだけれど、ちょっとプログラムがアレだなと思ったので、書き直してみた。
主要な部分だけを比較すると、元の記事のプログラムは、

  /**
   * filePathのファイルを読み込んでbyte配列に格納して返す。java.ioを使う。
   * @param filePath
   * @return ファイルの中身
   */
  public static byte[] readUsingIO(String filePath)
      throws IOException {
    BufferedInputStream in = new BufferedInputStream(
        new FileInputStream(filePath));
    byte[] data = new byte[in.available()];
    int size = in.read(data);
    System.out.println("size="+size);
    return data;
  }

  /**
   * filePathのファイルを読み込んでbyte配列に格納して返す。java.nioを使う。
   * @param filePath
   * @return ファイルの中身
   */
  public static byte[] readUsingNIO(String filePath)
      throws IOException {
    FileInputStream in = new FileInputStream(filePath);
    FileChannel channel = in.getChannel();

    ByteBuffer buf = ByteBuffer.allocateDirect((int)channel.size());
    channel.read(buf);

    buf.flip();
    byte[] data = new byte[buf.capacity()];
    buf.get(data);

    channel.close();
    return data;
  }

となっているわけだけれど、ダイレクトバッファを使っている点とバッファに読み込んだデータを再度配列に書いている点。後、FileInputStream#read()は配列に直接書き込むのに対して、FileChannel#read()はByteBufferのインターフェースを介して書き込むのでその分効率が悪いわけだから、FileChannel#read()を使うのはよくない。
というわけで、書き直したプログラムが以下。ファイルのメモリマップを取得して、そこから直接byte配列にコピーするという処理。

  /**
   * filePathのファイルを読み込んでbyte配列に格納して返す。java.nioを使う。
   * @param filePath
   * @return ファイルの中身
   */
  public static byte[] readUsingNIO(String filePath)
      throws IOException {
    File file = new File(filePath);
    byte[] data = new byte[(int) file.length()];

    FileInputStream in = new FileInputStream(file);
    FileChannel channel = in.getChannel();
    ByteBuffer buf = channel.map(FileChannel.MapMode.READ_ONLY,0,file.length());
    buf.get(data);
    System.out.println("nio size=" + data.length);

    channel.close();
    return data;
  }

結果。単位は、byteとmsec。

100 1,000 10,000 100,000 1,000,000 10,000,000 100,000,000
java.io 0 0 1 0 7 58 350
java.nio(新) 5 1 0 1 4 37 218
java.nio(旧) 1 0 0 1 6 67 424