即使我将缓冲区大小设置为 1,BufferedInputStream 也会读取整个文件一次?

BufferedInputStream reads the entire file once even though I set the buffer size to 1?

提问人:kaka 提问时间:7/27/2023 更新时间:7/28/2023 访问量:150

问:

我正在运行下面的代码来尝试了解 BufferedInputStream 在 Java 中的工作原理。我将缓冲区大小设置为 1,并期望缓冲区读取文件 465 次,因为这是文件中的字符量。但是,它读取文件一次。我发现更改缓冲区读取文件的次数,您将字节数组更改为 1。在这种情况下,它会读取文件 465 次。我不明白为什么即使我将缓冲区大小设置为 1,缓冲区也会读取文件一次。为什么数组“does”决定缓冲区读取文件的次数?

 File f = new File("runs");
    
    if(!f.exists()) {
        f.createNewFile();
    }
    

    
    FileInputStream input = new FileInputStream(f);
    

    BufferedInputStream b = new BufferedInputStream(input, 1);
    
    byte[] does = new byte[1000];
    
    int i = b.read(does);
    
    int x = 0;
    String tmp;
    while(i != -1) {
        tmp = new String(does, StandardCharsets.UTF_8);
        if(!tmp.equalsIgnoreCase("\n")) {
            System.out.print(tmp);
        }else {
            System.out.println(tmp);
        }

        x++;
        i = b.read(does);
    }
    System.out.println(x);
    
    
    
}
Java 缓冲 文件inputstream bufferedinputstream

评论

0赞 tgdavies 7/27/2023
阅读 Javadoc 了解 .它从流中读取,直到传递到的缓冲区已满或达到 EOF。中的缓冲区是一个完全不同的缓冲区,用于通过减少对文件执行的读取次数来提高性能。InputStream.read(byte[])readBufferedInputStream
0赞 Paul Pazderski 7/27/2023
@tgdavies 让我补充一个小而重要的细节:它可以读取直到缓冲区已满,并且通常会读取,但不能保证。甚至在达到 EOF 之前。必须遵循返回值以检查实际读取了多少字节。这也是为什么像 Apache Commons IO 这样的库会提供像 IOUtils.readFully 这样的帮助函数
1赞 7/27/2023
@tgdavies是正确的。为什么数组“does”决定缓冲区读取文件的次数?这是因为 b.read(does) 等价于 b.read(does, 0, does.length)。这意味着如果 does.length=1000,它将尝试读取 1000 字节(如果可用)。
0赞 Reilas 7/28/2023
以下是有关 BufferedInputStream 用例的问答,可能会阐明为什么会以这种方式设置它。stackoverflow.com/questions/3122422/....

答:

1赞 Lunatic 7/27/2023 #1

BufferedInputStreamInputStream 中方法的差异。read()

让我们从输入流中读取单个字节的数据并将其作为值返回,该值将在 2 条件下被阻止,检测到流的末尾或引发异常。 While 为传递的输入流添加缓冲。InputStream.readintBufferedInputStream

区别在于以块的形式从底层输入流中读取数据并将其存储在内部缓冲区中,因此当您调用方法时,它会从其缓冲区返回下一个字节,因此区别在于数据调用开销量,其中通过将多个数据请求分组到来自底层输入流的较少调用中来减少它。BufferedInputStreamread()BufferedInputStream

为什么 BufferedInputStream 即使在指定缓冲区大小时也会读取整个文件?

实际上不会,即使指定了缓冲区大小,它也不一定会将整个文件读入缓冲区,实际上它会以块或块的形式将数据从文件读入缓冲区,其大小最多是缓冲区的大小,文件被读取的次数取决于文件的大小和使用的缓冲区的大小, 在共享代码段中,您将缓冲区的大小指定为 1,这就是您一次获得一个字节的原因,这应该有所不同,在您的情况下,例如BufferedInputStream

BufferedInputStream b = new BufferedInputStream(input, 1024);

评论

0赞 kaka 7/28/2023
“您将缓冲区的大小指定为 1,这就是您一次获得一个字节的原因”但它不会一次获得一个字节。IT 一次读取由 464 个字节组成的整个文件。
0赞 DuncG 7/28/2023 #2

请注意,它并不总是读取到它自己的内部缓冲区。BufferedInputStream

如果你查看构造函数的源代码,你会看到当你在内部分配时,它会创建一个内部缓冲区:new BufferedInputStream(input, 1)buf = new byte[1];

BufferedInputStream b = new BufferedInputStream(input, 1);
// Internally causes b.buf = new byte[1];

在运行下一行时,未从基础文件中读取任何内容:input

byte[] does = new byte[1000];
int i = b.read(does);

如果你查看源代码,你会发现它调用。通常,读取将使用内部的内容(如果可用)并复制到 .但是在第一次读取中,它是空的,因此它使用最大的缓冲区。因此,这意味着它要么被填充并被复制到,要么像您的情况一样 (1000 >= 1),因此它填充并且不填充内部 .read(byte[]ba)BufferedInputStream.read(byte[] ba, int off, int len)bufbufbabufbuf.length / lenbufbadoesbuf

因此,即使您将内部缓冲区大小指定为仅 1 个字节,对基础文件的首次访问也可能最多为 1000 字节(如果可从 )。input

顺便说一句,您不应该像上面那样转换 UTF8 流,因为您可能会在 UTF-8 字符编码的中途损坏输入流 - 显然改用。Reader

评论

0赞 kaka 7/28/2023
因此,它将内部增益与“does”进行比较,并且因为“does”是 1000,所以它会读取所有文件?如果读取中的增益决定了一切,那么内部增益的目的是什么?此外,在这种情况下,我认为 BufferInputStream 比 FileInputStream 没有优势,因为两者都可以使用方法 read(byte[]ba) 中的缓冲区。
0赞 DuncG 7/28/2023
正确。如果总是直接读取大于默认缓冲区大小的 byte[],则使用 BufferedInputStream 是没有意义的,在这种情况下,您可以直接访问。input
0赞 DuncG 7/28/2023
请注意,一次读取一个字符(如 )或读取小于内部缓冲区的数组将使用内部缓冲区来获得缓冲的好处。int ch = b.read()