使用 BufferedReader.readLine() 读取 inputStream 的速度太慢了。

41

我正在使用以下代码。

BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line = null;

StringBuilder responseData = new StringBuilder();
while((line = in.readLine()) != null) {
    responseData.append(line);
}

但是读取200行需要超过12秒。

请帮个忙。


你有没有想过客户端或者网络可能是慢的原因呢? - Ingo
1
conn是什么?如果直接读取InputStream(到一个你将要丢弃的byte[]中),会更快吗? - Joachim Sauer
1
它是从哪里读取的?如果问题与缓冲区本身无关,我会感到惊讶。您是否在通过网络进行阅读?比如,从一个没有行终止符的网络连接获取数据,以至于 readLine 一直等待着找到可以识别为行结束的内容?或者来自真正发送数据缓慢的地方? - Gareth McCaughan
问题出在 while 循环中。我已经测量了时间。 - Lohit
6
您觉得考虑一下阅读过程不能比写作过程更快的想法没有意义吗?您已经测量了时间 - 当然,但似乎您无法解释您的测量结果。这就像我测量一辆红色汽车的速度,然后坚称是红色让它跑得那么快一样。 - Ingo
你是否曾经想过,in.readLine() 逐行读取内容,而换行符是分隔符,当你逐行连接字符串时 -> 你会丢失原始内容中的所有 '\r\n'? - msangel
2个回答

30

我强烈怀疑是因为你所连接的网络或web服务器存在问题,这不是BufferedReader的问题。 试着测量一下:

InputStream stream = conn.getInputStream();
byte[] buffer = new byte[1000];
// Start timing
while (stream.read(buffer) > 0)
{
}
// End timing

我认为你会发现它几乎与解析文本的时间相同。

请注意,您还应为InputStreamReader提供适当的编码-平台默认编码几乎肯定不是您应该使用的编码。


亲爱的Jon,我已经测试了时间。这个时间只是用于while循环。 - Lohit
6
是的,因为这是它实际从网络中读取的地方。除非这些行非常长,否则这不会是BufferedReader/InputStreamReader的负担。 - Jon Skeet

9

我有一个更长的测试要尝试。每读取一行并将其添加到列表中,平均需要160纳秒(这可能是您想要的,因为删除换行符没有什么用处)。

public static void main(String... args) throws IOException {
    final int runs = 5 * 1000 * 1000;

    final ServerSocket ss = new ServerSocket(0);
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                Socket serverConn = ss.accept();
                String line = "Hello World!\n";
                BufferedWriter br = new BufferedWriter(new OutputStreamWriter(serverConn.getOutputStream()));
                for (int count = 0; count < runs; count++)
                    br.write(line);
                serverConn.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }).start();

    Socket conn = new Socket("localhost", ss.getLocalPort());

    long start = System.nanoTime();
    BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
    String line;

    List<String> responseData = new ArrayList<String>();
    while ((line = in.readLine()) != null) {
        responseData.add(line);
    }
    long time = System.nanoTime() - start;
    System.out.println("Average time to read a line was " + time / runs + " ns.");
    conn.close();
    ss.close();
}

打印

Average time to read a line was 158 ns.

如果您想构建一个StringBuilder并保留换行符,我建议采用以下方法。
Reader r = new InputStreamReader(conn.getInputStream());
String line;

StringBuilder sb = new StringBuilder();
char[] chars = new char[4*1024];
int len;
while((len = r.read(chars))>=0) {
    sb.append(chars, 0, len);
}

仍然打印。
Average time to read a line was 159 ns.

在这两种情况下,速度受发送者而不是接收者的限制。通过优化发送者,我将此时间缩短到每行105纳秒。


1
使用第二种方法对我来说实际上稍微快了一点,谢谢。 - max4ever

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