数组中的读取/写入是否比一个字符一个字节的读取/写入更有效?

问题描述 投票:1回答:1
try(FileReader reader = new FileReader("input.txt")) {

    int c;
    while ((c = reader.read()) != -1)
        System.out.print((char)c);

} catch (Exception ignored) { }

在这段代码中,我逐字符读取一个字符。以某种方式一次将a读入一个char数组是否更有效率?换句话说,在读取数组时会发生任何优化吗?

例如,在此代码中,我有一个名为chararr数组,我一直读到其中,直到没有剩下要读取的内容为止。效率更高吗?

    try(FileReader reader = new FileReader("input.txt")) {

        int size;
        char[] arr = new char[100];
        while ((size = reader.read(arr)) != -1)
            for (int i = 0; i < size; i++)
                System.out.print(arr[i]);

    } catch (Exception ignored) { }

该问题适用于读/写两个字符/字节。

java optimization inputstream outputstream
1个回答
1
投票

取决于读者。答案可能是肯定的。无论是Reader还是InputStream都是真正的“原始”驱动程序(不只是包装另一个阅读器或inputstream的驱动程序,而是实际上正在与OS进行通信以获取数据的驱动程序)-可以很好地实现单字符[ C0]方法,方法是要求操作系统读取单个字符。

最后,您有一个磁盘,磁盘以块为单位返回数据。因此,如果您要求1个字节,则您有2种选择作为计算机:

  1. 向磁盘查询包含要读取的字节的块。将块存储在内存中一段时间​​。返回一个字节;在接下来的片刻中,如果来自同一块的字节请求更多,请从内存中存储的数据中返回,根本不用去询问磁盘。注意:这需要内存!谁分配?多少内存还可以?棘手的问题。操作系统倾向于提供低级工具,并且不喜欢仅仅为这些问题中的任何一个选择值。

  2. 向磁盘查询包含要读取的字节的块。从该块中找到所需的1个字节。忽略其余数据,仅返回该一个字节。如果稍后需要该块中的另一个字节...再次向磁盘询问整个块,然后重复此例程。

您获得的两种模型中的哪一种取决于许多因素:例如:它是哪种磁盘,您拥有什么操作系统,正在使用什么底层Java阅读器。但是,您可能会以第二种模式结束,这很可能是合理的,因为您可能会说,通常这是令人难以置信的缓慢,因为您最终会读取同一块4000多次而不是仅读取一次。

所以,如何解决这个问题?好吧,java也不知道操作系统在做什么,所以最安全的选择是让java

进行缓存。这样,您就不必依赖操作系统在做什么。

您可以自己编写,所以不要:

read()

您可以做:

for (int i = in.read(); i != -1; i = in.read()) {
    processOneChar((char) i);
}

更多代码,但是现在第二种情况(同一块被从磁盘读取很多次)不再发生;您已授予操作系统自由以最多4096个字符的价值返回给您的数据。

或者,使用Java内置的:BufferedX:

char[] buffer = new char[4096];
while (true) {
    int r = in.read(buffer);
    if (r == -1) break;
    for (int i = 0; i < r; i++) processOneChar(buffer[i]);
}

BufferedReader br = new BufferedReader(in); for (int i = br.read(); i != -1; i = br.read()) { processOneChar((char) i); } 的实现保证了Java将负责制作一些合理大小的缓冲区,以避免从磁盘上重新读取同一块。

NB:请注意,不应使用您正在使用的FileReader构造函数。它使用平台默认编码(任何时候将字节转换为字符,都会涉及编码),并且平台默认编码是无法测试的错误的秘诀,这些错误非常糟糕。请改用BufferedReader,或者最好使用新的API:

new FileReader(file, StandardCharsets.UTF_8)

请注意:

  1. 默认为UTF-8,因为与VM中的大多数位置不同,文件API的默认值为UTF-8。
  2. 立即制作一个缓冲的读取器,无需自己制作。
  3. 通过使用ARM块正确地管理资源(无论该代码如何退出,无论正常还是异常,都必须对其进行关闭管理。)>
  4. 因为涉及到BufferedX,所以不会冒“大量读取同一块”的性能漏洞的风险。
  5. NB:写入时适用相同的逻辑;诸如SSD之类的磁盘一次只能写入一个完整的块。现在,不仅要像蜜糖糖一样缓慢地写入,而且由于它们得到的写入次数有限,您还破坏了磁盘。

© www.soinside.com 2019 - 2024. All rights reserved.