Java 为什么使用BufferedInputStream逐字节读取文件比使用FileInputStream快?

Java 为什么使用BufferedInputStream逐字节读取文件比使用FileInputStream快?,java,file-io,inputstream,fileinputstream,Java,File Io,Inputstream,Fileinputstream,我试图使用FileInputStream将一个文件读入数组,一个~800KB的文件读入内存大约需要3秒钟。然后,我尝试了相同的代码,只是将FileInputStream包装到BufferedInputStream中,耗时约76毫秒。为什么使用BufferedInputStream一个字节一个字节地读取文件要快得多,即使我仍在一个字节一个字节地读取它?下面是代码(其余代码完全无关)。请注意,这是“快速”代码。如果需要“慢速”代码,可以删除BufferedInputStream: BufferedI

我试图使用FileInputStream将一个文件读入数组,一个~800KB的文件读入内存大约需要3秒钟。然后,我尝试了相同的代码,只是将FileInputStream包装到BufferedInputStream中,耗时约76毫秒。为什么使用BufferedInputStream一个字节一个字节地读取文件要快得多,即使我仍在一个字节一个字节地读取它?下面是代码(其余代码完全无关)。请注意,这是“快速”代码。如果需要“慢速”代码,可以删除BufferedInputStream:


BufferedInputStream的速度快30倍以上。远不止这些。那么,这是为什么呢?有没有可能使此代码更高效(不使用任何外部库)?

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
,无论要执行多少次
read()
调用(仍然
32768
),您只需要
4

<>如何加快速度,你可能想考虑java 7的Nio>代码>文件通道< /C>类,但是我没有证据支持这个。

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

围绕FileInputStream包装的BufferedInputStream将以大块的形式从FileInputStream请求数据(我认为默认情况下大约512字节。)因此,如果您一次读取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));
    }

啊,我明白了,我应该在询问之前先检查API。所以它只是一个8K的内部缓冲区。这很有意义。谢谢。至于“更高效”部分,这是不必要的,但我认为我的代码在某种程度上可能过于冗余。我想不是。@user1007059不客气。请注意,如果使用
FileInputStream
read(byte[],int,int)
方法,直接使用
byte[>8192]
你不需要一个
BufferedInputStream
包装它。@SotiriosDelimanolis何时使用
read()
逐字节,何时使用
read(byte[])
字节数组。我认为读取数组总是更好。那么你能给我一个例子,在哪里使用
read()
逐字节或
read(byte[])
byte数组。或者
BufferedInputStream
?@UnKnown没有一个很好的例子。可能第一个字节包含一些关于文件内容的标志或其他元数据。我认为任何人都不会使用
read()读取整个文件
@emily
BufferedInputStream
在代码请求读取更少字节(不一定只有一个字节)时速度更快每次都比缓冲区大小大。
BufferedInputStream
表现乐观,读取的数据比您需要的要多,因此,当您返回时,它已经有了下一批数据。可能是,但对于大多数平台来说都是。8K相同。这个示例很好。但是,用这样的数据检查执行时间-基准测试绝对不正确例如,使用JMH来正确检查。
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));
    }