缓冲输入流的用法

39

在开始之前,我要提醒大家:我对Java完全是个新手。我学习了一段时间的PHP编程,但现在准备开发一个桌面应用程序,因为各种原因我决定使用Java。

我正在进行的这个应用程序处于起步阶段(不到5个类),需要从本地文件读取字节。通常情况下,文件大小不超过512kB(但将来可能更大)。目前,我使用FileInputStream把文件读入三个字节数组中,这完全满足我的需求。然而,我看到有人提到了BufferedInputStream,想知道我目前的做法是否最佳,或者是否应该同时使用BufferedInputStream

我已经做了一些研究,在Stack Overflow上也看了一些问题,但仍然难以理解何时使用和不使用BufferedInputStream。在我的情况下,我首先读取字节的第一个数组只有几个字节(少于20个字节)。如果这些字节的数据正常,那么我就会把文件的剩余部分读入大小不同的另外两个字节数组中。

我也听到很多人提到了性能分析,以确定每种情况下哪种方法更有效,但我没有性能分析的经验,也不知道从哪里开始。我希望得到一些建议。

非常抱歉我的帖子这么长,但我真的想学习和理解怎样才是最佳方法。我总是有一个坏习惯,就是对自己的决定反复思考,因此我希望能得到一些反馈。谢谢!

6个回答

88

如果你一直在进行小读操作,那么BufferedInputStream会显著提高性能。在非缓冲流上的每个读请求通常都会导致一个系统调用来读取请求的字节数。每次系统调用的开销可能是数千条机器指令。而缓冲流通过一次性将(例如)最多8k字节的内容读入内部缓冲区,然后从该缓冲区分发字节来减少系统调用数量,这可以大大降低系统调用的数量。

但是,如果你一直在进行大读操作(例如8k或更多),那么BufferedInputStream会略微减慢速度。通常情况下不会减少系统调用的次数,并且缓冲引入额外的数据复制步骤。

对于你的使用案例(先读取20字节块,然后读取大块内容),我会说使用BufferedInputStream更有可能降低而不是提高性能。但最终结果取决于实际的读取模式。


1
然而,如果您持续进行大量读取(例如8k或更多),那么BufferedInputStream会减慢速度。如何做到呢? - Asif Mushtaq
看看这段代码!调用中有额外的间接层,需要额外的工作来检查缓冲区是否有内容等。幸运的是,代码足够智能,可以尽可能地避免不必要的复制,至少在InputStream API方面是如此。因此,相对的减速很小,但是它是可以测量的。 - Stephen C

5

如果你使用相对较大的数组一次读取数据块,那么BufferedInputStream将会引入一个浪费的副本。(要记住,read并不一定读取整个数组 - 你可能需要DataInputStream.readFully). BufferedInputStream的优势在于进行大量小的读取。


1
我想我明白你的意思。让我问你另一个问题。我看到FileInputStream的构造函数需要一个byte[]作为参数。目前,我正在使用for循环来读取所需的字节,但是我认为使用这个参数会更有效率?我还假设通过for循环不断调用FileInputStream的read方法就是你所说的大量小读取?很抱歉听起来像个新手,但出于某种原因,我很难完全理解这一点。感谢您的回答! - Jason Watkins
@mastermosaj,你可能正在看到ByteArrayInputStream的构造函数,它是一个通过byte[]读取而不进行实际I/O的InputStream。如果你正在逐字节地读取你的byte[],那么你可能会发现使用BufferedInputStreamByteArrayInputStream可以简化你的代码,但会带来一些性能损失。(注意不要混淆使用BufferedInputStream和使用底层流,因为前者会缓冲。 - Tom Hawtin - tackline

1

BufferedInputStream 会提前读取更多文件内容。据我理解,它会提前做更多的工作,例如一次读取连续大块的磁盘数据,而不是在一个紧密循环中进行多次读取。

至于性能分析 - 我喜欢 NetBeans 内置的分析器。它非常容易上手。 :-)


1
谢谢你的建议。我听到有人在NetBeans中提到了分析器。我开始使用NetBeans,但是现在我已经切换到使用纯文本编辑器。我觉得这样可以更好地学习语言。你还有其他建议吗? - Jason Watkins
文本编辑器很好用,但如果你要向客户计费,那就像踩着垃圾车一样。如果你想避免在 IDE 中进行分析,可以尝试使用 hprof:http://java.sun.com/developer/technicalArticles/Programming/HPROF.html - Jubal
1
谢谢@jskaggz。我会查看hprof。顺便说一下,我正在为自己制作这个应用程序,所以我没有真正的时间表,但我同意如果是为客户制作,我肯定会使用IDE来加快速度。 - Jason Watkins

1

我不能对性能分析发表意见,但根据我开发Java应用程序的经验,我发现使用任何缓冲类 - BufferedInputStream、StringBuffer - 我的应用程序速度异常快。因此,即使是处理最小的文件或字符串操作,我也会使用它们。


1
当您使用BufferedInputStream时,通常会指定缓冲的特定大小块,还是让它自动决定? - Jason Watkins
这取决于情况。正如Stephen C所说,如果这个数字与系统调用中使用的数据页大小(比如4k)不匹配,那么你刚刚制造了一个瓶颈。可以把它想象成用铲子填充沙袋。如果你在铲子上舀太多或太少的沙子,那么你会降低效率/性能。顺便说一下,我是写好代码的支持者。但如果你刚开始学习,先让它能工作再进行优化也没有什么不对的。这些问题可能导致无穷无尽的追溯。 - Jason McCreary
@JasonMcCreary 什么时候使用read()一个字节一个字节地读取,什么时候使用read(byte[])字节数组。我认为读取数组总是更好的。你能否给我一个例子,在哪里使用read()字节逐个 OR read(byte[])字节数组 OR BufferedInputStream - Asif Mushtaq

0
以下方法对我来说非常有效,可以预先填充缓冲输入流。
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;
}

-3
    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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接