在Java中逐行读取文本文件的最快方法

9

为了处理日志,我的应用需要逐行读取文本文件。 起初我使用了BufferedReader的readLine()函数,但在互联网上看到它读取文件时速度较慢。
后来我尝试使用FileInputStream与FileChannel和MappedByteBuffer一起使用,但这种情况下没有类似于readLine()的函数,所以我需要搜索换行符并进行处理:

    try {
        FileInputStream f = new FileInputStream(file);
        FileChannel ch = f.getChannel( );
        MappedByteBuffer mb = ch.map(FileChannel.MapMode.READ_ONLY, 0L, ch.size());
        byte[] bytes = new byte[1024];
        int i = 0;
        while (mb.hasRemaining()) {
            byte get = mb.get();
            if(get == '\n') {
                if(ra.run(new String(bytes)))
                    cnt++;
                for(int j = 0; j<=i; j++)
                    bytes[j] = 0;
                i = 0;
            }
            else
                bytes[i++] = get;
        }
    } catch(Exception ex) {
        ex.printStackTrace();
    }

我知道这可能不是实现它的好方法,但是当我只是以字节读取文本文件时,它比使用BufferedReader快3倍,但调用new String(bytes)会创建一个新的字符串,并使程序比使用BufferedReader还要慢。
因此,我想问一下,逐行读取文本文件的最快方法是什么? 有人说BufferedReader是解决此问题的唯一方法。
附注:ra是dk.brics.Automaton库中RunAutomaton的实例。

1
BufferedReader 对于您的需求真的太慢了吗?如果您必须使用 Java 编码,它可能是最干净、最易维护的解决方案之一。 - jcomeau_ictx
如果 BufferedReader 对于您的应用程序来说真的太慢了,那么您应该考虑不使用Java或其他托管语言...(但我怀疑这是否是情况) - ordag
1
Aaron的回答即将因为只有链接而被删除,所以我会把它放在这里作为评论:“看看这个链接,它包含了各种方法的速度比较。” - Stewie Griffin
5个回答

19

我非常怀疑BufferedReader会造成显著的开销。添加您自己的代码可能效率至少与使用BufferedReader一样低,而且很可能还是错误的。

例如,在您给出的代码中,您调用了new String(bytes),它总是会从1024个字节中创建一个字符串,使用平台默认编码……这不是一个好主意。当然,您之后清除了数组,但您的字符串仍然会包含许多'\0'字符-这意味着大量浪费的空间,除了其他方面。您应该至少限制创建字符串的字节数组的部分(这也意味着您不需要在之后清除数组)。

您是否实际上已经尝试使用BufferedReader并发现它速度太慢?通常情况下,您应该首先编写最简单的能够实现目标的代码,然后检查它是否足够快……特别是如果您不这样做的唯一理由是"从互联网上读到的"未指定的资源。您想让我找到数百个人误导性的性能建议吗?:)

作为替代方案,您可以考虑查看GuavaFiles.readLines()重载,它采用LineProcessor


1
我已经尝试了BufferedReader,它的表现很好,但程序的要求是要非常快,所以我正在尝试找出哪种解决方案是最快和最好的适用于我的实现。 - Yoni
2
@Yoni:「非常快」是一个相当模糊的要求。你有没有任何证据表明瓶颈是BufferedReader而不是(更有可能的)物理磁盘速度? - Jon Skeet
如果我以字节方式读取相同的文件,比使用“BufferedReader”快3倍。我的硬盘速度约为150mb/s,而我的程序读取速度为30mb/s。 - Yoni
@Yoni:嗯...这有点令人惊讶。您使用的编码是什么,您的机器规格是什么?您是否在调试器中运行,或者做了任何可能会减慢速度的操作?您是否使用字符串? - Jon Skeet
看起来唯一合适的解决方案是使用 BufferedReader。因为你提供了最有帮助和完整的信息,我接受了你的答案。 - Yoni
显示剩余2条评论

4

使用普通的BufferedReader我获得了100+ MB/s的速度。很可能从磁盘读取数据的速度是瓶颈,所以读取方式不会有太大差异。

BufferedReader并不是唯一的解决方案,但对于99%的用例来说它已经足够快了,为什么要把事情搞得更复杂呢?


1

0
我有一个非常简单的循环,使用BufferedReader从sdcard上的文件读取约2000行(50k字节),在Galaxy Tab 2上以调试模式读取所有行大约需要100毫秒,还不错。然后我在循环中放了一个Scanner,时间就飙升了(几十秒),还有很多GC_CONCURANT消息。
Scanner scanner = new Scanner(line);
int eventType = scanner.nextInt(16);

至少在我的情况下,问题出在扫描器上,我猜我需要以另一种方式扫描整数,但我不知道为什么它会如此缓慢。


0
根据this的Stack Overflow帖子,你可能也想尝试一下Scanner类。

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