我还在学习Java中的面向对象编程。我正在研究java.io.Reader.skip
的Java实现,并且我想知道为什么它实现了它的实现方式。特别是我对这些事情有疑问,我注意到了:
skip(long)
的缓冲区是Reader对象的字段,而不是方法中的普通变量。Integer.MAX_VALUE
2147,483,647。特别是,Java的实现使用了8192。java.io.InputStream
实现跳过相同的方式。现在,我个人认为缓冲区是一个字段的原因是,由于重复重新初始化,缓冲区不必重复进行垃圾回收。这可能会更快地跳过。
缓冲区长度越小我认为这与读取器阻塞更短的时间有关,但是由于Reader是同步的,这真的会产生影响吗?
以相同方式实现它的字节流可能是为了保持一致性。这三件事我的假设是否正确?
总而言之,我的问题是:关于字符数组使用字段而不是变量,平均速度差异有多大?使用Integer.MAX_VALUE
作为最大缓冲区长度是不是一样的?并且在字节流的for循环中使用无参数read
方法更好更容易,因为其他read
方法只调用无参数read
?
很抱歉,如果我的问题是一个奇怪的问题,但我认为我可以通过这个问题了解面向对象编程。
答案 0 :(得分:2)
一次读取一个字符的效率会低得多 - 每个字节跳过一次方法调用,这对于大型跳过(大量开销)通常是不好的。
暂存缓冲区大小很容易回答:如果要从文件中跳过2G,是否真的要分配Integer.MAX_VALUE
RAM 块?
至于确切的大小,以及是否使用实例varialbe,这是一个依赖于实现的折衷方案。您正在阅读选择8192成员的实现。一些实现具有较小的本地实现(可以看到512 here)。
标准中没有任何内容需要任何这些实现细节,因此根本不要依赖它们。
如果你打算做类似的事情,基准不同的方法,并在你的具体情况下选择最好的妥协。
答案 1 :(得分:2)
关于字符数组使用字段而不是变量,平均速度差异有多大?
这肯定会有所不同,从JVM到JVM,但重复分配一个8K阵列可能并不像保留一个那么便宜。当然,这里隐藏的教训是,不应该抓住读者,即使是封闭的读者,因为它们会带来8K的惩罚。
使用Integer.MAX_VALUE作为最大缓冲区长度不是一样的吗?
缓冲区必须预先分配,并且分配2Gb阵列似乎是一种过度杀伤力。请记住,分页的原因是分摊读取呼叫的成本 - 有时会变成本机操作。
在字节流的for循环中使用无参数读取方法是不是更好更容易,因为其他读取方法只调用无参数读取?
无法保证底层流是缓冲的,因此可能会产生大量的每次呼叫开销。
最后,请记住java.io类有许多不足之处,所以不要假设那里的所有内容都有充分的理由。
答案 2 :(得分:1)
你忘记了2 ^ 31 - 1的缓冲区是2 GB的内存,必须分配,然后才能用于其他任何东西
分配2 千兆字节的大型连续字节块对于读取字节数而言是过度的,并且可能导致内存不足的情况
8 kB的最大内存缓冲区是更好的替代方案,也是更好的权衡,因为它只会被分配一次(并且它将在每次跳过操作时重复使用)
btw在java.io.InputStream
中,skipbuff是静态的,只有一次分配,但由于没有从中读取(它只是用作只写内存),所以不必担心比赛
答案 3 :(得分:1)
对于InputStream,您经常使用允许更有效跳过的子类,并且这些子类适当地覆盖skip
方法。但对于那些没有有效跳过方法的子类(如压缩或解压缩输入流),skip
方法是基于读取实现的,因此不是每个子类都必须这样做。
在java.io包中有几种关于如何实现它的策略:
跳过基本流:
FilterInputStream.skip()
只是委托给源流。不过,我不太确定这是多么有用。DataInputStream
不会覆盖skip
,但有另一个名为skipBytes()
的方法,它执行相同的操作(仅适用于int
个参数)。它委托给底层源流。* BufferedInputStream.skip()
覆盖它,首先跳过自己缓冲区中的现有内容,然后在基本流上调用skip()
(如果没有设置mark()
- 如果有标记,则必须将所有内容读入缓冲区以支持reset()
)。PushbackInputStream.skip
首先跳过其回传缓冲区,然后调用super.skip(这是FilterInputStream.skip(),见上文)。重置索引:
ByteArrayInputStream
可以轻松支持跳过,只需设置下一步的位置即可。原生魔法:
skip
作为本机方法。我认为这将是最有用的规范示例。阅读所有内容并将其扔掉:
LineNumberInputStream.skip()
必须阅读所有内容以计算行数。 (我不知道这个类是否存在。请改用LineNumberReader。)ObjectInputStream
不会覆盖skip
,但有另一个名为skipBytes()
的方法,它执行相同的操作(仅适用于int
个参数)。它委托给一个内部类(BlockDataInputStream.skip),后者依次从底层流中读取块数据的Object流协议。InputStream
中的默认实现。 SequenceInputStream
和PipedInputStream
也会使用此功能。让我们看看Reader
类。原则上,适用相同的策略。
跳过基础读者/流:
FilterReader.skip()
这样做。PushBackReader
首先跳过自己的后推缓冲区,然后是基础读取器。重置一些索引:
阅读所有内容并将其扔掉:
Reader.skip()
,也由PipedReader
使用。
InputStreamReader
,“简单地跳过基本流”方法仅适用于固定字节数字符集(即ISO-8859系列,UTF-16和一些类似的字符集),而不适用于UTF-8 ,UTF-32或每个字符具有可变字节数的其他字符集,因为我们必须读取所有字节以了解它们实际上代表了多少字符。这也适用于其子类FileReader
。read
,但会填充其内部缓冲区,该缓冲区从基本流中读取。)