为什么使用Scanner读取输入时比使用BufferedReader慢?

12

我了解Scanner的用途,也知道何时使用Scanner和BufferedReader。我阅读了一个与之相关但有些不同的问题Scanner vs. BufferedReader

为什么使用Scanner从输入中读取数据会很慢? 我猜这可能与Scanner中有一个小缓冲区有关,但我并不确定。 原始问题来自于Codechef,但我对那个解决方案不感兴趣。

下面是一个带有给定输入的代码示例:

  • 7 3
  • 1
  • 51
  • 966369
  • 7
  • 9
  • 999996
  • 1

代码如下:

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class Main {

    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String[] s = br.readLine().split(" "); 
        int numberOfLines = Integer.parseInt(s[0]);
        int divideNumber = Integer.parseInt(s[1]);
        int count = 0;

        for (int i = 0; i < numberOfLines; i++) {
            String number = br.readLine();
            if (number.length() < 11) {
                int num = Integer.parseInt(number);
                if (num % divideNumber == 0) {
                    count++;
                }
            } 
        }
        System.out.println(count);
    }
}

如果我使用扫描器读取相同的代码,速度会很慢。


Scanner 基于正则表达式解析, BufferedReaderchar 执行简单检查。 - zapl
2
如果你想要快速解决这个问题,你需要完全跳过BufferedReaderInteger.parseInt()。它仍然会在你将其转换为int之前创建一个不必要的临时String对象,这需要比直接从流中构建int花费更多的时间。 - zapl
我来这里是因为我需要读取一个大约有1300万行的CSV文件。首先使用了Scanner,但它花费了超过2个小时的时间。改用BufferedReader后只花了几分钟。在一台现代的MacBook Pro上。我知道会有速度差异,但它们如此之大让我感到困惑。编辑:第二次直接运行只花了几秒钟。也许JIT正在起作用。 - Robert Sjödahl
5个回答

10

通常来说,高级别的类/方法比低级别的类/方法慢。
同样地,你可能会问为什么使用“正则表达式”进行搜索比使用“String.indexOf()”进行搜索要慢。实际上,我在此处看到过这样的问题。

你的类/方法越专业化,它的性能就越好。
例如,它只做一件简单的事情,但能快速、高效地完成该任务。
相比之下,更通用的类/方法可能需要执行10-20个不同的任务,因此速度会更慢,但由于其功能更强大。

我在这里是以一般情况来讲述,我自己并没有比较“Scanner”和“BufferedReader”的速度。


3
除了已经提到的Scanner聚焦于成为一把瑞士军刀,它相当复杂,在简单情况下被BufferedReader覆盖,这会增加额外的负担。这就像派遣一个航空母舰去杀一只老鼠。

0

关于为什么Scanner速度较慢的一些高票提示可以在Scanner vs. BufferedReader中找到。

这种性能差异在某些情况下可能非常关键,比如竞技编程。因此,Codeforces有许多帖子,其中包括自定义更快的输入解析器,例如herehere(带基准测试)和here


0

Scanner内置函数解析标记,而BufferedReader只读取输入。解析标记会带来额外的开销...


这是正确的,但如果我使用Scanner读取示例代码,根据文档,令牌将是整行:“扫描器使用分隔符模式将其输入拆分为标记,默认情况下匹配空格。然后可以使用各种next方法将生成的标记转换为不同类型的值。” - Gábor Csikós
所以它之所以慢,是因为它需要额外的操作来获取一个令牌吗? - Gábor Csikós
请查看此链接:http://en.allexperts.com/q/Java-1046/2009/2/Difference-Scanner-Method-Buffered.htm - Devavrata

0

正如您在问题中提到的,其中提出了一个不同的问题。扫描器执行额外的任务,例如解析整数和字符。缓冲读取器读取原始输入。它读取的就是它所给出的。

希望我能帮到您,

Jarod


我认为它可以解析基本类型和字符串,这意味着它并不执行所有操作,只是调用所需操作。 - Gábor Csikós

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