为什么使用 BufferedInputStream 一个字节一个字节地读取文件比使用 FileInputStream 快?

我试图通过使用 FileInputStream 将一个文件读入一个数组,而将一个 ~ 800KB 的文件读入内存大约需要3秒钟。然后,我尝试了相同的代码,只是将 FileInputStream 封装成 BufferedInputStream,花了大约76毫秒。为什么用 BufferedInputStream 一个字节一个字节地读取文件要快得多,尽管我仍然在一个字节一个字节地读取它?下面是代码(代码的其余部分完全不相关)。注意,这是“快速”代码。如果你想要“缓慢”的代码,你可以删除 BufferedInputStream:

InputStream is = null;


try {
is = new BufferedInputStream(new FileInputStream(file));


int[] fileArr = new int[(int) file.length()];


for (int i = 0, temp = 0; (temp = is.read()) != -1; i++) {
fileArr[i] = temp;
}

BufferedInputStream 的速度快了30多倍。远不止这些。那么,为什么会这样,是否有可能使这段代码更加高效(不使用任何外部库) ?

61111 次浏览

FileInputStream中,方法 read()读取一个字节:

/**
* Reads a byte of data from this input stream. This method blocks
* if no input is yet available.
*
* @return     the next byte of data, or <code>-1</code> if the end of the
*             file is reached.
* @exception  IOException  if an I/O error occurs.
*/
public native int read() throws IOException;

这是对操作系统的本机调用,操作系统使用磁盘读取单个字节。这是个大行动。

对于 BufferedInputStream,该方法委托给一个重载的 read()方法,该方法读取 8192的字节数,并将它们缓冲到需要的时候。它仍然只返回一个字节(但保留其他字节)。这样,BufferedInputStream对操作系统进行的从文件读取的本机调用就更少了。

例如,您的文件长度为 32768字节。要使用 FileInputStream获取内存中的所有字节,需要对操作系统进行 32768本机调用。有了 BufferedInputStream,你只需要 4,无论你将做的 read()调用的数量(仍然是 32768)。

至于如何使其更快,您可能需要考虑 Java7的 NIO FileChannel类,但是我没有证据支持这一点。


注意: 如果直接使用 FileInputStreamread(byte[], int, int)方法,使用 byte[>8192]时不需要 BufferedInputStream包装它。

一个包装在 FileInputStream 中的 BufferedInputStream 将以大块(我认为默认是512字节左右)请求来自 FileInputStream 的数据因此,如果一次读取1000个字符,FileInputStream 只需要两次访问磁盘。这样会快很多!

这是因为磁盘访问的成本。让我们假设您将有一个大小为8kb 的文件。在没有 BufferedInputStream 的情况下,读取这个文件需要访问磁盘8 * 1024次。

此时,BufferedStream 出现在场景中,充当 FileInputStream 和要读取的文件之间的中间人。

在一个镜头中,将获得大块的字节默认为8kb 到内存,然后 FileInputStream 将从这个中间人读取字节。 这将减少操作的时间。

private void exercise1WithBufferedStream() {
long start= System.currentTimeMillis();
try (FileInputStream myFile = new FileInputStream("anyFile.txt")) {
BufferedInputStream bufferedInputStream = new BufferedInputStream(myFile);
boolean eof = false;
while (!eof) {
int inByteValue = bufferedInputStream.read();
if (inByteValue == -1) eof = true;
}
} catch (IOException e) {
System.out.println("Could not read the stream...");
e.printStackTrace();
}
System.out.println("time passed with buffered:" + (System.currentTimeMillis()-start));
}




private void exercise1() {
long start= System.currentTimeMillis();
try (FileInputStream myFile = new FileInputStream("anyFile.txt")) {
boolean eof = false;
while (!eof) {
int inByteValue = myFile.read();
if (inByteValue == -1) eof = true;
}
} catch (IOException e) {
System.out.println("Could not read the stream...");
e.printStackTrace();
}
System.out.println("time passed without buffered:" + (System.currentTimeMillis()-start));
}