参考this和this问答,考虑到Java的Scanner同时支持字符编码和缓冲,是否需要将
InputStream
包装成InputStreamReader
,然后再将其传递给Scanner?如果是这样,是否需要先将 InputStreamReader
包装成 BuffereedReader
?
换句话说,是否存在以下代码不是多余的场景。
FileInputStream inputStream = new FileInputStream(fileName);
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedStreamReader = new BufferedReader(inputStreamReader);
Scanner scanner = new Scanner(bufferedStreamReader);
我正在学习 Java(从 Python),并试图更好地理解所有各种 IO 类的相似点、差异和用例。
(编辑1):我知道我们可以使用
FileReader
将前两行替换为一行 - 但这就是 FileReader
本身在内部所做的事情,所以这个例子(和这个问题)的目的是理解不同 IO 类的底层关系 - 因此显式调用 FileInputStream
use。
(编辑2):这个 Java 教程实际上做了这种包装。
这是一个有争议的问题;你很少需要扫描仪(它并没有真正按照你想象的那样做。它按照它的规格说明做,但是,阅读它。它是怪异 - 它不是特别适合,嗯,任何东西,真的)。而且您不需要此代码 - 这是旧的文件 API。有新的了:
Path p = Path.of("/path/to/some/dir/someFileInDir.txt");
p = p.getParent().resolve("someOtherFile.txt");
try (var in = Files.newBufferedReader(p)) {
while (true) {
String line = in.readLine();
if (line == null) break;
}
}
当然,你可以用
Files.newBufferedReader(Path.of("/path/to/file.txt"))
来写它。
其价值:
FileInputStream
不缓冲。如果您调用 .read()
(即“请读取 1 个字节”方法),它将向底层操作系统请求仅一个字节。在现代 SSD 上,这意味着操作系统将读取整个块,然后丢弃除您想要的 1 个字节之外的所有内容。是的,如果您或您使用的任何代码有意调用 .read()
,则应该将它们包装在缓冲区中,但是如果您只是要使用足够大的字节数组(4096 左右就足够了,更不用说更多了),没有必要。.read(byte[])
InputStreamReader
确实,默认 1024。
没用,但 1024 有点低。 BufferedInputStream 默认为 8192,这是一个更好的数字。尽管如此,与从非缓冲基于块的源请求单个字节相比,1024 的性能提高了约 1024 倍。假设这些块的长度为 8192 字节,如果没有中间缓冲区,Scanner 只会慢 8 倍左右。假设 100% 的瓶颈是“从磁盘读取”部分。可能是这样,磁盘速度很慢。 这都是相关的
if你正在手动滚动你不应该的缓冲堆栈(使用Scanner
- 检查API,那里有很多,通常你可以一次性制作你需要的东西,尽管你不能制作扫描仪一次),并使用
Files.newBufferedX
,但你不应该使用。