有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

为什么是java。伊奥。读者#斯基普是如何实现的?

我还在学习Java中的面向对象编程。我在看java.io.Reader.skip的Java实现,我想知道为什么它的实现方式是这样的。我特别注意到,我对这些事情有疑问:

  1. 用于skip(long)的缓冲区是Reader对象的字段,而不是方法中的正常变量
  2. 最大缓冲区长度远小于Integer.MAX_VALUE2147483647。特别是,Java的实现使用8192
  3. java.io.InputStream实现同样的方式

现在,我个人认为缓冲区是一个字段的原因是,缓冲区不必因为被重复初始化而被重复垃圾收集。这可能会使跳绳更快

缓冲区长度变小,我认为这与使读卡器阻塞更短的时间有关,但既然读卡器是同步的,这真的会有区别吗

字节流以同样的方式实现它,可能是为了一致性。我对这三件事的假设正确吗

总而言之,我的问题是:对于字符数组,使用字段而不是变量,平均速度会有多大差异?使用Integer.MAX_VALUE作为最大缓冲区长度不是一样吗?在字节流的for循环中使用no-parameterread方法不是更好更容易,因为其他read方法只调用no-parameterread

抱歉,如果我的问题很奇怪,但我认为通过这个问题我可以学到很多关于面向对象编程的知识


共 (3) 个答案

  1. # 1 楼答案

    对于InputStream,通常会有一些子类,它们允许更有效的跳过,这些子类会适当地覆盖skip方法。但是对于那些没有有效跳过方法的子类(比如压缩或解压缩输入流),方法skip是基于读取实现的,所以不是每个子类都必须这样做

    关于如何在java.io包中实现这一点,有几种策略:

    跳过基本流:

    • FilterInputStream.skip()只需委托给源流。不过,我不确定这有多有用

    • DataInputStream不重写skip(),但有另一个名为skipBytes()的方法,它做同样的事情(不过,只针对int参数)。它委托给底层的源流

    • BufferedInputStream.skip()覆盖这个,首先跳过它自己缓冲区中的现有内容,然后在基本流上调用skip()(如果没有mark()集,如果有标记,它必须将所有内容读入缓冲区以支持reset()

    • PushbackInputStream.skip()首先跳过其后推缓冲区,然后调用super.skip()(即FilterInputStream.skip(),见上文)

    重置索引:

    • ByteArrayInputStream只需设置下一步阅读的位置,就可以轻松支持跳过

    • StringBufferInputStream(这是StringReader的一个弃用版本)只需重置索引即可支持跳过

    本土魔法:

    • FileInputStreamskip()作为native方法。我认为这将是最有用的典型例子

    阅读所有内容并将其扔掉:

    • LineNumberInputStream.skip()必须阅读所有内容才能数清行数。(我不知道这个类的存在。改用LineNumberReader。)

    • ObjectInputStream不重写skip(),但有另一个名为skipBytes()的方法,它做同样的事情(不过,只针对int参数)。它委托给一个内部类(BlockDataInputStream.skip()),该类反过来从底层流中读取数据,这与块数据的对象流协议有关

    InputStream:

    中的默认实现
    • 这也被SequenceInputStreamPipedInputStream使用

    让我们看看Reader类。原则上,同样的策略也适用:

    跳过基本读取器/流:

    • FilterReader.skip()就是这样做的

    • PushBackReader首先跳过它自己的回推缓冲区,然后跳过基本读取器

    重置一些索引:

    • StringReader(这个实际上支持向后跳转)

    • CharArrayReader

    阅读所有内容并将其扔掉:

    • 默认的Reader.skip(),也被PipedReader使用

    • 对于InputStreamReader“简单跳过基流”方法仅适用于固定字节计数的字符集(即ISO-8859系列、UTF-16和一些类似的字符集),而不适用于UTF-8UTF-32或每个字符具有可变字节数的其他字符集,因为我们必须读取所有字节,才能知道它们实际上代表了多少个字符。这也适用于它的子类FileReader

    • BufferedReader(它不调用自己的read(),而是填充从基流读取的内部缓冲区)

    • LineNumberReader(它必须这样做才能跟踪行号)

  2. # 2 楼答案

    您忘记了2^31-1的缓冲区是必须分配的2GB内存,然后不能用于任何其他用途

    分配一个2gigabytes的大连续字节块对于以字节为单位的读取来说是过度的,它可能会导致内存不足的情况

    8 kB的最大内存缓冲区是一个更好的选择,也是一个更好的折衷方案,因为它只分配一次(每次跳过操作都会重复使用)

    顺便说一句,skipbuff是静态的,只分配过一次,但由于没有读取(它只是用作只写内存),因此不需要担心竞争

  3. # 3 楼答案

    About how much of a difference in speed on average does it make to use a field rather than a variable for character arrays?

    这在不同的JVM中肯定会有所不同,但重复分配一个8K数组可能不如保持一个数组便宜。当然,这里隐藏的教训是,你不应该抓住读者,即使是封闭的读者,因为他们会受到8K的惩罚

    Wouldn't it be just the same to use Integer.MAX_VALUE as the maximum buffer length?

    缓冲区必须预先分配,而分配2Gb阵列似乎是一种过度消耗。请记住,分页的原因是分摊读调用的成本,读调用有时会变成本机操作

    Isn't it better and easier to use the no-parameter read method in a for-loop for byte streams since the other read methods just call the no-parameter read?

    不能保证底层流是缓冲的,因此这可能会产生大量的每次调用开销

    最后,请记住java。io类有很多很多缺陷,所以不要认为所有的东西都有充分的理由