BufferedInputStream 的用法

Usage of BufferedInputStream

提问人:Jason Watkins 提问时间:6/26/2010 最后编辑:Erik KaplunJason Watkins 更新时间:8/18/2023 访问量:23733

问:

让我在这篇文章的开头提出一个警告。在Java方面,我是一个完全的初学者。我已经断断续续地编写 PHP 一段时间了,但我已经准备好制作一个桌面应用程序,所以出于各种原因,我决定使用 Java。

我正在处理的应用程序处于初始阶段(少于 5 个类),我需要从本地文件中读取字节。通常,文件当前小于 512kB(但将来可能会变大)。目前,我正在使用 a 将文件读入三个字节数组,这完全满足了我的要求。但是,我看到了一个提到,并且想知道我目前这样做的方式是否最好,或者我是否也应该使用一个。FileInputStreamBufferedInputStreamBufferedInputStream

我已经做了一些研究,并在这里阅读了一些关于 Stack Overflow 的问题,但我仍然无法理解何时使用和不使用 .在我的情况下,我读入字节的第一个数组只有几个字节(小于 20 个)。如果我收到的数据在这些字节中是好的,那么我会将文件的其余部分读入另外两个不同大小的字节数组中。BufferedInputStream

我还听说很多人提到分析,看看在每种特定情况下哪个更有效,但是,我没有分析经验,我不确定从哪里开始。我也希望得到一些建议。

我很抱歉写了这么长的帖子,但我真的很想学习和理解做这些事情的最佳方法。我总是有一个坏习惯,就是事后猜测我的决定,所以我希望得到一些反馈。谢谢!

Java 文件输入流 bufferedinputstream

评论


答:

1赞 Jubal 6/26/2010 #1

BufferedInputStream 会提前读取所需的更多文件。据我了解,它提前做了更多的工作,比如,1 个大的连续磁盘读取与在一个紧密的循环中做很多工作。

就分析而言 - 我喜欢 netbeans 中内置的分析器。这真的很容易上手。:-)

评论

1赞 Jason Watkins 6/26/2010
感谢您的建议。我听说有人提到 NetBeans 中的 profilier。我开始使用 NetBeans,但是,我暂时改用纯文本编辑器。我觉得我以这种方式学到了更多关于这门语言的知识。你还有其他建议吗?
0赞 Jubal 6/26/2010
文本编辑器很棒,但如果您要向客户收费,这有点像踩自卸卡车。如果您想避免在 ide 中进行性能分析,可以尝试 hprof: java.sun.com/developer/technicalArticles/Programming/HPROF.html
1赞 Jason Watkins 6/27/2010
谢谢@jskaggz。我会检查 hprof。顺便说一句,我是为自己制作这个应用程序,所以我并没有真正在时间表上,但我同意,如果它是为客户准备的,我肯定会使用 ide 来加快它的速度。
1赞 Jason McCreary 6/26/2010 #2

我不能谈论分析,但根据我开发 Java 应用程序的经验,我发现使用任何缓冲区类(BufferedInputStream、StringBuffer)我的应用程序都非常快。正因为如此,我甚至将它们用于最小的文件或字符串操作。

评论

1赞 Jason Watkins 6/26/2010
使用 BufferedInputStream 时,通常是否为其指定特定大小的块进行缓冲,还是让它自动决定?
0赞 Jason McCreary 6/26/2010
这要视情况而定。正如 Stephen C 上面所说,如果这个数字与系统调用中使用的数据页面大小(比如 4k)不太吻合,那么你就会通过制造瓶颈来搬起石头砸自己的脚。把它想象成用铲子装满沙袋。如果你在铲子上舀太多或太少的沙子,你只会降低效率/性能。顺便说一句,我是写好代码的倡导者。但是,如果您刚刚开始,那么让它工作然后再进行优化并没有错。这些东西可能是兔子洞。
0赞 Asif Mushtaq 4/1/2016
@JasonMcCreary 何时逐字节使用,何时使用字节数组。因为我认为读取数组总是更好。那么你能给我举个例子在哪里使用逐字节或字节数组。或。?read()read(byte[])read()read(byte[])BufferedInputStream
5赞 Tom Hawtin - tackline 6/26/2010 #3

如果你使用一个相对较大的数组来一次读取一个块的数据,那么只会引入一个浪费的副本。 (记住,不一定读取所有的数组 - 你可能想要)。获胜的地方在于进行大量小阅读。BufferedInputStreamreadDataInputStream.readFullyBufferedInputStream

评论

1赞 Jason Watkins 6/26/2010
我想我明白你在说什么。让我再问你一个问题。我看到一个 FileInputStream 的构造函数,它采用 byte[] 作为参数。目前,我正在使用 for 循环来读取所需的字节,但是,我认为使用此参数会更有效吗?我还假设使用 for 循环不断从 FileInputStream 调用 read,这就是您所说的大量小读取的意思吗?我很抱歉听起来很菜鸟,但出于某种原因,我很难完全理解这一点。感谢您的回答!
0赞 Tom Hawtin - tackline 6/26/2010
@mastermosaj 您可能会看到 的构造函数,它是读取 的,因此没有实际的 I/O。如果你是逐字节读取,那么你可能会发现使用或简化你的代码会付出一些性能代价。(请注意,不要将 using 与 using 底层流混用,因为前者会缓冲。ByteArrayInputStreamInputStreambyte[]byte[]BufferedInputStreamByteArrayInputStreamBufferedInputStream
88赞 Stephen C 6/26/2010 #4

如果您始终如一地进行小读取,那么 a 将为您提供更好的性能。对无缓冲流的每个读取请求通常会导致系统调用操作系统以读取请求的字节数。执行系统调用的开销可能是每个系统调用的数千条机器指令。缓冲流通过对内部缓冲区执行一次大型读取(例如)最多 8k 字节,然后从该缓冲区传出字节来减少这种情况。这可以大大减少系统调用的次数。BufferedInputStream

但是,如果您一直在进行大量读取(例如 8k 或更多),那么 a 会减慢速度。通常不会减少系统调用的数量,缓冲会引入额外的数据复制步骤。BufferedInputStream

在您的用例中(您首先读取一个 20 字节的块,然后读取大量大块),我会说使用 a 更有可能降低性能而不是提高性能。但归根结底,这取决于实际的读取模式。BufferedInputStream

评论

1赞 Asif Mushtaq 4/1/2016
However, if you are consistently doing large reads (e.g. 8k or more) then a BufferedInputStream slows things.如何?
0赞 Stephen C 4/1/2016
看代码!调用中有额外的间接级别,额外的工作检查缓冲区中是否有任何内容等。幸运的是,代码足够智能,可以尽可能避免不必要的复制,使用 InputStream API。因此,相对减速很小,但可以测量。
-3赞 Octavians 6/26/2013 #5
    import java.io.*;
    class BufferedInputStream
    {
            public static void main(String arg[])throws IOException
            {
                FileInputStream fin=new FileInputStream("abc.txt");
                BufferedInputStream bis=new BufferedInputStream(fin);
                int size=bis.available();
                while(true)
                {
                        int x=bis.read(fin);
                        if(x==-1)
                        {
                                bis.mark(size);
                                System.out.println((char)x);
                        }
                }
                        bis.reset();
                        while(true)
                        {
                                int x=bis.read();
                                if(x==-1)
                                {
                                    break;
                                    System.out.println((char)x);
                                }
                        }

            }

    }

评论

3赞 Mr_and_Mrs_D 7/27/2013
对不起 - 这是什么?
0赞 Mike 7/4/2023 #6

以下内容非常适合我预填充缓冲的输入流。

private BufferedInputStream readBeforehand(S3Object object) throws IOException {
    int length = min(object.getObjectMetadata().getContentLength(), BUFFER_MAX_SIZE);
    BufferedInputStream bis = new BufferedInputStream(object.getObjectContent(), length);
    bis.mark(length);
    for (int i = 0; i < length; i++)
        if (bis.read() == EOF) break;
    bis.reset();
    return bis;
}