数组中的 reading/writing 是否比 reading/writing 一个 char/byte 一个一个地高效?
Is reading/writing in an array more efficient than reading/writing a char/byte one by one?
try(FileReader reader = new FileReader("input.txt")) {
int c;
while ((c = reader.read()) != -1)
System.out.print((char)c);
} catch (Exception ignored) { }
在这段代码中,我逐个读取一个字符。以某种方式一次将 a 读入一个字符数组是否更有效?换句话说,读取数组时是否会发生任何类型的优化?
例如,在这段代码中,我有一个名为 arr
的 char
数组,我读入它直到没有剩余可读内容。是不是更有效率?
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) { }
该问题适用于 reading/writing 和 chars/bytes。
取决于 reader。不过,答案可能是肯定的。 Reader 或 InputStream 是实际的 'raw' 驱动程序(不只是包装另一个 reader 或输入流的驱动程序,而是实际与 OS 对话的驱动程序获取数据) - 它可以通过要求 OS 读取单个字符来很好地实现单字符 read()
方法。
最后,你有一个磁盘,磁盘 return 块中的数据。所以如果你要求 1 个字节,你有 2 个选项作为计算机:
向磁盘询问包含要读取的字节的块。将块存储在内存中的某个地方一段时间。 Return一个字节;在接下来的几分钟里,如果更多的字节请求来自同一个块,return 来自内存中存储的数据,根本不用问磁盘。注意:这需要内存!谁来分配?内存多大合适?棘手的问题。 OSes 倾向于提供低级工具,不喜欢为这些问题中的任何一个选择值。
向磁盘询问包含要读取的字节的块。从此块中找到所需的 1 个字节。忽略其余数据,return 只是那一个字节。如果过一会儿从该块中请求另一个字节...再次向磁盘请求整个块,然后重复此例程。
你得到这两种模型中的哪一种取决于很多因素:例如:它是什么类型的磁盘,你有什么OS,底层java reader是什么你在用。但是你最终进入第二种模式是合理的,也就是说,正如你可能知道的那样,通常非常慢,因为你最终读取同一个块 4000 多次而不是一次。
那么,如何解决这个问题?好吧,java 也不知道 OS 在做什么,所以最安全的做法是让 java 进行缓存。那么你就不会依赖 OS 正在做的任何事情。
你可以自己写,所以不是:
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]);
}
更多代码,但现在第二种情况(同一个块从磁盘上读取了很多次)不再发生;你给了 OS 自由 return 给你最多 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,因为文件 API 默认为 UTF-8,这与 VM 中的大多数地方不同。
- 立即制作缓冲reader,无需自己制作。
- 通过使用 ARM 块正确管理资源(无论此代码如何退出,无论是正常还是异常,都确保资源关闭)。
- 因为涉及 BufferedX,所以没有 'read the same block a lot' 性能漏洞的风险。
注意:写的时候也是一样的逻辑; SSD 等磁盘一次只能写入整个块。现在它不仅写起来像糖蜜一样慢,而且还会毁掉你的磁盘,因为它们的写入次数有限。
try(FileReader reader = new FileReader("input.txt")) {
int c;
while ((c = reader.read()) != -1)
System.out.print((char)c);
} catch (Exception ignored) { }
在这段代码中,我逐个读取一个字符。以某种方式一次将 a 读入一个字符数组是否更有效?换句话说,读取数组时是否会发生任何类型的优化?
例如,在这段代码中,我有一个名为 arr
的 char
数组,我读入它直到没有剩余可读内容。是不是更有效率?
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) { }
该问题适用于 reading/writing 和 chars/bytes。
取决于 reader。不过,答案可能是肯定的。 Reader 或 InputStream 是实际的 'raw' 驱动程序(不只是包装另一个 reader 或输入流的驱动程序,而是实际与 OS 对话的驱动程序获取数据) - 它可以通过要求 OS 读取单个字符来很好地实现单字符 read()
方法。
最后,你有一个磁盘,磁盘 return 块中的数据。所以如果你要求 1 个字节,你有 2 个选项作为计算机:
向磁盘询问包含要读取的字节的块。将块存储在内存中的某个地方一段时间。 Return一个字节;在接下来的几分钟里,如果更多的字节请求来自同一个块,return 来自内存中存储的数据,根本不用问磁盘。注意:这需要内存!谁来分配?内存多大合适?棘手的问题。 OSes 倾向于提供低级工具,不喜欢为这些问题中的任何一个选择值。
向磁盘询问包含要读取的字节的块。从此块中找到所需的 1 个字节。忽略其余数据,return 只是那一个字节。如果过一会儿从该块中请求另一个字节...再次向磁盘请求整个块,然后重复此例程。
你得到这两种模型中的哪一种取决于很多因素:例如:它是什么类型的磁盘,你有什么OS,底层java reader是什么你在用。但是你最终进入第二种模式是合理的,也就是说,正如你可能知道的那样,通常非常慢,因为你最终读取同一个块 4000 多次而不是一次。
那么,如何解决这个问题?好吧,java 也不知道 OS 在做什么,所以最安全的做法是让 java 进行缓存。那么你就不会依赖 OS 正在做的任何事情。
你可以自己写,所以不是:
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]);
}
更多代码,但现在第二种情况(同一个块从磁盘上读取了很多次)不再发生;你给了 OS 自由 return 给你最多 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,因为文件 API 默认为 UTF-8,这与 VM 中的大多数地方不同。
- 立即制作缓冲reader,无需自己制作。
- 通过使用 ARM 块正确管理资源(无论此代码如何退出,无论是正常还是异常,都确保资源关闭)。
- 因为涉及 BufferedX,所以没有 'read the same block a lot' 性能漏洞的风险。
注意:写的时候也是一样的逻辑; SSD 等磁盘一次只能写入整个块。现在它不仅写起来像糖蜜一样慢,而且还会毁掉你的磁盘,因为它们的写入次数有限。