从一个千兆字节的文件中读取

时间:2013-08-18 04:25:59

标签: java file-io fileinputstream

对于大型文件或管道流,缓冲区支持的解决方案(BufferedInputStream / ByteArrayInputStream)显然不是可行的方法。如果有人能告诉我处理这种情况的推荐方法,我将不胜感激。

我能想到这一点 - 但可能不是那里最好或最有效的方法:

public class Streams {
  public static void main(String[] args) throws IOException {
    DataInputStream reader=null;
    try{
      try {
        reader=new DataInputStream(new FileInputStream("/path/file"));
      } catch (FileNotFoundException e1) {
        throw e1;
      }
      while(true) {
        try {
          byte a=reader.readByte();
        } 
        catch(EOFException e) {
          //consume
        }
        catch (IOException e) {
          //throw
          throw e;
        }
        //do something
      }
    }
    finally {
      try {
        reader.close();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
  }
}

2 个答案:

答案 0 :(得分:6)

对大文件使用BufferedInputStreamBufferedReader没有任何根本性的错误。实际上,如果您需要一次读取/处理文件一个字节或字符,这是一种自然的方法。如果您在读取它们后对字节/字符执行了大量“工作”(即此应用程序的瓶颈不是读取输入流),那么缓冲流可能就好了。

另一方面,使用ByteArrayInputStream是一个糟糕的选择,原因有两个:

  • 您需要在之前将整个文件加载(或映射)到内存中,然后才能创建ByteArrayInputStream
  • 字节数组大小上限为2 ^ 31个字节...即2Gb。

事实上,您使用DataInputStream的建议版本与使用BufferedInputStream没有什么不同。 DataInputStream使用内部缓冲区的方式与BufferedInputStream的方式非常相似。

我的记忆不正确。实际上DataInputStream.readByte()是无缓冲的。因此,您建议的版本将比使用BufferedInputStream的版本慢得多。基本上,您的版本中的每个readByte()调用都会进行一次系统调用。这将使阅读变得非常非常缓慢。


获得显着加速的唯一方法是使用NIO BufferChannel API读取文件。与传统API相比,这些API减少了内存中复制的数量。缺点是这些API使用起来更加笨拙。

这预先假定读取输入文件的主要瓶颈。

答案 1 :(得分:2)

FileInputStream包裹在BufferedInputStream中没有问题。根据Java库源代码,默认缓冲区大小仅为8192字节,因此使用它不会耗尽内存。

在您当前的代码中,DataInputStream.readByte()的每次调用都会调用FileInputStream.read(),这是一个缓慢的本机函数,可以转到C和操作系统。

相反,您应该将FileInputStream包装在缓冲区大小为几百KB的BufferedInputStream中。这样,DataInputStream.readByte()将调用BufferedInputStream.read(),这相当快(并且使用纯Java实现)。