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


try(FileReader reader = new FileReader("input.txt")) {
int c;
while ((c = reader.read()) != -1)
System.out.print((char)c);
} catch (Exception ignored) { }

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

例如,在此代码中,我有一个名为arrchar数组,我读入其中,直到有注释可供阅读。效率更高吗?

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) { }

该问题适用于读取/写入字符/字节。

取决于读者。不过,答案可能是肯定的。无论读取器或输入流是实际的"原始"驱动程序(不仅包装另一个读取器或输入流,而且实际与操作系统通信以获取数据的驱动程序( - 它都可以通过要求操作系统读取单个字符来实现单字符read()方法。

最后,你有一个磁盘,磁盘以块的形式返回数据。因此,如果您要求 1 个字节,则作为计算机有 2 个选项:

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

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

您获得的两种模型中的哪一种取决于许多因素:例如:它是哪种磁盘,您拥有什么操作系统,您使用的是什么底层 Java 阅读器。但是,您最终可能会进入第二种模式,也就是说,正如您可能知道的那样,通常非常慢,因为您最终会读取相同的块4000 +次,而不是一次。

那么,如何解决这个问题呢?好吧,Java也不知道操作系统在做什么,所以最安全的选择是让Java做缓存。然后,您就不依赖于操作系统正在执行的任何操作。

你可以自己写,而不是:

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

你可以做:

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]);
}

更多的代码,但现在第二种情况(相同的块从磁盘上读取大量次(不能再发生;你已经给了操作系统自由,可以返回给你多达4096个字符的数据。

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

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

BufferedReader的实现保证了java将负责制作一些合理大小的缓冲区,以避免从磁盘上重新读取同一块。

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

Path p = Paths.get("C:/file.txt");
try (BufferedReader br = Files.newBufferedReader(p)) {
for (int i = br.read(); i != -1; i = br.read()) {
processOneChar((char) i);
}
}

请注意,这:

默认为 UTF-8
  1. ,因为文件 API 默认为 UTF-8,这与 VM 中的大多数位置不同。
  2. 立即制作缓冲阅读器,无需自己制作。
  3. 通过使用 ARM 块正确管理资源(确保无论此代码如何退出,无论是正常还是异常,它都已关闭(。
  4. 由于涉及 BufferedX,因此不存在"大量读取同一块"性能漏洞的风险。

注意:写入时也适用相同的逻辑;SSD 等磁盘一次只能写入整个块。现在,它不仅像糖蜜一样缓慢,而且您还破坏了磁盘,因为它们的写入次数有限。

最新更新