Java NIO读书笔记 -- (三) 缓冲区基础

一、Buffer类简介

  1. 一个Buffer对象是固定数量的数据的容器。其作用是一个存储器,或者分段运输区,在这里数据可被存储并在之后用于检索。

  2. 缓冲区的工作与通道紧密联系。通道是I/O传输发生时通过的入口, 而缓冲区是这些数据传输的来源或目标。

对于离开缓冲区的传输,要传递出去的数据被置于一个缓冲区,被传送到通道。
对于传回缓冲区的传递,一个通道将数据放置在你所提供的缓冲区中。

  1. 这种在协同对象(通常是您所写的对象以及一到多个 Channel 对象)之间进行的缓冲区数据传递是高效数据处理的关键。

二、Buffer类层次结构

Buffer类层次结构

三、缓冲区基础

  1. 缓冲区是包在一个对象内的基本数据元素数组
  2. Buffer 类相比一个简单数组的优点是它将关于数据的数据内容和信息包含在一个单一的对象中。
  3. Buffer 类以及它专有的子类定义了一个用于处理数据缓冲区的 API。

四、缓冲区属性

所有的缓冲区都具有四个属性来提供关于其所包含的数据元素的信息。

1.容量(Capacity)

缓冲区能够容纳的数据元素的最大数量。这一容量在缓冲区创建时被设定,并且永远不能被改变

2.上界(Limit)

缓冲区的第一个不能被读或写的元素。或者说,缓冲区中现存元素的计数。

3.位置(Position)

下一个要被读或写的元素的索引。位置会自动由相应的get()和put()函数更新。

4.标记(Mark)

一个备忘位置。调用mark()来设定mark=position。调用reset()设定position = mark。标记在设定前是未定义的(Undefined)

这四个属性之间的关系:

0 <= mark <= position <= limit <= capacity

五、新创建ByteBuffer示例

位置被设为 0,而且容量和上界被设为 10,ࡊ好经过缓冲区能够容纳的最后一个字节。 标记最初未定义。容量是固定的,但另外的三个属性可以在使用缓冲区时改变。

六、缓冲区API

1. Buffer 类的方法签名:

public abstract class Buffer {

    /**
     * The characteristics of Spliterators that traverse and split elements
     * maintained in Buffers.
     */
    static final int SPLITERATOR_CHARACTERISTICS =
        Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.ORDERED;

    // Invariants: mark <= position <= limit <= capacity
    private int mark = -1;
    private int position = 0;
    private int limit;
    private int capacity;

    // Used only by direct buffers
    // NOTE: hoisted here for speed in JNI GetDirectBufferAddress
    long address;

    // Creates a new buffer with the given mark, position, limit, and capacity,
    // after checking invariants.
    //
    Buffer(int mark, int pos, int lim, int cap) {       // package-private
        if (cap < 0)
            throw new IllegalArgumentException("Negative capacity: " + cap);
        this.capacity = cap;
        limit(lim);
        position(pos);
        if (mark >= 0) {
            if (mark > pos)
                throw new IllegalArgumentException("mark > position: ("
                                                   + mark + " > " + pos + ")");
            this.mark = mark;
        }
    }

    /**
     * Returns this buffer's capacity.
     *
     * @return  The capacity of this buffer
     */
    public final int capacity() {
        return capacity;
    }

    /**
     * Returns this buffer's position.
     *
     * @return  The position of this buffer
     */
    public final int position() {
        return position;
    }

    /**
     * Sets this buffer's position.  If the mark is defined and larger than the
     * new position then it is discarded.
     *
     * @param  newPosition
     *         The new position value; must be non-negative
     *         and no larger than the current limit
     *
     * @return  This buffer
     *
     * @throws  IllegalArgumentException
     *          If the preconditions on <tt>newPosition</tt> do not hold
     */
    public final Buffer position(int newPosition) {
        if ((newPosition > limit) || (newPosition < 0))
            throw new IllegalArgumentException();
        position = newPosition;
        if (mark > position) mark = -1;
        return this;
    }

    /**
     * Returns this buffer's limit.
     *
     * @return  The limit of this buffer
     */
    public final int limit() {
        return limit;
    }

    /**
     * Sets this buffer's limit.  If the position is larger than the new limit
     * then it is set to the new limit.  If the mark is defined and larger than
     * the new limit then it is discarded.
     *
     * @param  newLimit
     *         The new limit value; must be non-negative
     *         and no larger than this buffer's capacity
     *
     * @return  This buffer
     *
     * @throws  IllegalArgumentException
     *          If the preconditions on <tt>newLimit</tt> do not hold
     */
    public final Buffer limit(int newLimit) {
        if ((newLimit > capacity) || (newLimit < 0))
            throw new IllegalArgumentException();
        limit = newLimit;
        if (position > limit) position = limit;
        if (mark > limit) mark = -1;
        return this;
    }

    /**
     * Sets this buffer's mark at its position.
     *
     * @return  This buffer
     */
    public final Buffer mark() {
        mark = position;
        return this;
    }

    /**
     * Resets this buffer's position to the previously-marked position.
     *
     * <p> Invoking this method neither changes nor discards the mark's
     * value. </p>
     *
     * @return  This buffer
     *
     * @throws  InvalidMarkException
     *          If the mark has not been set
     */
    public final Buffer reset() {
        int m = mark;
        if (m < 0)
            throw new InvalidMarkException();
        position = m;
        return this;
    }

    /**
     * Clears this buffer.  The position is set to zero, the limit is set to
     * the capacity, and the mark is discarded.
     *
     * <p> Invoke this method before using a sequence of channel-read or
     * <i>put</i> operations to fill this buffer.  For example:
     *
     * <blockquote><pre>
     * buf.clear();     // Prepare buffer for reading
     * in.read(buf);    // Read data</pre></blockquote>
     *
     * <p> This method does not actually erase the data in the buffer, but it
     * is named as if it did because it will most often be used in situations
     * in which that might as well be the case. </p>
     *
     * @return  This buffer
     */
    public final Buffer clear() {
        position = 0;
        limit = capacity;
        mark = -1;
        return this;
    }

    /**
     * Flips this buffer.  The limit is set to the current position and then
     * the position is set to zero.  If the mark is defined then it is
     * discarded.
     *
     * <p> After a sequence of channel-read or <i>put</i> operations, invoke
     * this method to prepare for a sequence of channel-write or relative
     * <i>get</i> operations.  For example:
     *
     * <blockquote><pre>
     * buf.put(magic);    // Prepend header
     * in.read(buf);      // Read data into rest of buffer
     * buf.flip();        // Flip buffer
     * out.write(buf);    // Write header + data to channel</pre></blockquote>
     *
     * <p> This method is often used in conjunction with the {@link
     * java.nio.ByteBuffer#compact compact} method when transferring data from
     * one place to another.  </p>
     *
     * @return  This buffer
     */
    public final Buffer flip() {
        limit = position;
        position = 0;
        mark = -1;
        return this;
    }

    /**
     * Rewinds this buffer.  The position is set to zero and the mark is
     * discarded.
     *
     * <p> Invoke this method before a sequence of channel-write or <i>get</i>
     * operations, assuming that the limit has already been set
     * appropriately.  For example:
     *
     * <blockquote><pre>
     * out.write(buf);    // Write remaining data
     * buf.rewind();      // Rewind buffer
     * buf.get(array);    // Copy data into array</pre></blockquote>
     *
     * @return  This buffer
     */
    public final Buffer rewind() {
        position = 0;
        mark = -1;
        return this;
    }

    /**
     * Returns the number of elements between the current position and the
     * limit.
     *
     * @return  The number of elements remaining in this buffer
     */
    public final int remaining() {
        return limit - position;
    }

    /**
     * Tells whether there are any elements between the current position and
     * the limit.
     *
     * @return  <tt>true</tt> if, and only if, there is at least one element
     *          remaining in this buffer
     */
    public final boolean hasRemaining() {
        return position < limit;
    }

    /**
     * Tells whether or not this buffer is read-only.
     *
     * @return  <tt>true</tt> if, and only if, this buffer is read-only
     */
    public abstract boolean isReadOnly();

    /**
     * Tells whether or not this buffer is backed by an accessible
     * array.
     *
     * <p> If this method returns <tt>true</tt> then the {@link #array() array}
     * and {@link #arrayOffset() arrayOffset} methods may safely be invoked.
     * </p>
     *
     * @return  <tt>true</tt> if, and only if, this buffer
     *          is backed by an array and is not read-only
     *
     * @since 1.6
     */
    public abstract boolean hasArray();

    /**
     * Returns the array that backs this
     * buffer  <i>(optional operation)</i>.
     *
     * <p> This method is intended to allow array-backed buffers to be
     * passed to native code more efficiently. Concrete subclasses
     * provide more strongly-typed return values for this method.
     *
     * <p> Modifications to this buffer's content will cause the returned
     * array's content to be modified, and vice versa.
     *
     * <p> Invoke the {@link #hasArray hasArray} method before invoking this
     * method in order to ensure that this buffer has an accessible backing
     * array.  </p>
     *
     * @return  The array that backs this buffer
     *
     * @throws  ReadOnlyBufferException
     *          If this buffer is backed by an array but is read-only
     *
     * @throws  UnsupportedOperationException
     *          If this buffer is not backed by an accessible array
     *
     * @since 1.6
     */
    public abstract Object array();

    /**
     * Returns the offset within this buffer's backing array of the first
     * element of the buffer  <i>(optional operation)</i>.
     *
     * <p> If this buffer is backed by an array then buffer position <i>p</i>
     * corresponds to array index <i>p</i> + <tt>arrayOffset()</tt>.
     *
     * <p> Invoke the {@link #hasArray hasArray} method before invoking this
     * method in order to ensure that this buffer has an accessible backing
     * array.  </p>
     *
     * @return  The offset within this buffer's array
     *          of the first element of the buffer
     *
     * @throws  ReadOnlyBufferException
     *          If this buffer is backed by an array but is read-only
     *
     * @throws  UnsupportedOperationException
     *          If this buffer is not backed by an accessible array
     *
     * @since 1.6
     */
    public abstract int arrayOffset();

    /**
     * Tells whether or not this buffer is
     * <a href="ByteBuffer.html#direct"><i>direct</i></a>.
     *
     * @return  <tt>true</tt> if, and only if, this buffer is direct
     *
     * @since 1.6
     */
    public abstract boolean isDirect();


    // -- Package-private methods for bounds checking, etc. --

    /**
     * Checks the current position against the limit, throwing a {@link
     * BufferUnderflowException} if it is not smaller than the limit, and then
     * increments the position.
     *
     * @return  The current position value, before it is incremented
     */
    final int nextGetIndex() {                          // package-private
        if (position >= limit)
            throw new BufferUnderflowException();
        return position++;
    }

    final int nextGetIndex(int nb) {                    // package-private
        if (limit - position < nb)
            throw new BufferUnderflowException();
        int p = position;
        position += nb;
        return p;
    }

    /**
     * Checks the current position against the limit, throwing a {@link
     * BufferOverflowException} if it is not smaller than the limit, and then
     * increments the position.
     *
     * @return  The current position value, before it is incremented
     */
    final int nextPutIndex() {                          // package-private
        if (position >= limit)
            throw new BufferOverflowException();
        return position++;
    }

    final int nextPutIndex(int nb) {                    // package-private
        if (limit - position < nb)
            throw new BufferOverflowException();
        int p = position;
        position += nb;
        return p;
    }

    /**
     * Checks the given index against the limit, throwing an {@link
     * IndexOutOfBoundsException} if it is not smaller than the limit
     * or is smaller than zero.
     */
    final int checkIndex(int i) {                       // package-private
        if ((i < 0) || (i >= limit))
            throw new IndexOutOfBoundsException();
        return i;
    }

    final int checkIndex(int i, int nb) {               // package-private
        if ((i < 0) || (nb > limit - i))
            throw new IndexOutOfBoundsException();
        return i;
    }

    final int markValue() {                             // package-private
        return mark;
    }

    final void truncate() {                             // package-private
        mark = -1;
        position = 0;
        limit = 0;
        capacity = 0;
    }

    final void discardMark() {                          // package-private
        mark = -1;
    }

    static void checkBounds(int off, int len, int size) { // package-private
        if ((off | len | (off + len) | (size - (off + len))) < 0)
            throw new IndexOutOfBoundsException();
    }
}

2. Buffer链式调用

buffer.mark();
buffer.position(5);
buffer.reset();

可以被简写为:

buffer.mark().position(5).reset();

3.isReadOnly()函数

所有的缓冲区都是可读的,但并非所有都可写。
每个具体的缓冲区类都通过执行 isReadOnly()来标示其是否允许该缓存区的内容被修改。

4.存取

缓冲区管理着固定数目的数据元素。但在任何特定的时刻,我们可能 只对缓冲区中的一部分元素感兴趣。换句话说,在我们想清空缓冲区之前,我们可能只使用了 缓冲区的一部分。

我们需要能够追踪添加到缓冲区内的数据元素的数量,放入下一个元 素的位置等等的方法。位置属性做到了这一点。

它在调用 put()时指出了下一个数据元素应 该被插入的位置,或者当 get()被调用时指出下一个元素应从何处检索。

public abstract class ByteBuffer extends Buffer implements Comparable {
    // This is a partial API listing
    public abstract byte get( ); 
    public abstract byte get (int index); 
    public abstract ByteBuffer put (byte b); 
    public abstract ByteBuffer put (int index, byte b);
}

5. 填充

将代表“Hello"字符串的 ASCII 码载入一个名为 buffer 的 ByteBuffer 对象中。

buffer.put((byte)'H').put((byte)'e').put((byte)'l').put((byte)'l').put((byte)'o');
五次调用put()之后的缓冲区

如果我们想在不丢失位置的情况下进行一些 更改该怎么办呢?put()的绝对方案可以达到这样的目的。 假设我们想将缓冲区中的内容从 “Hello"的 ASCII 码更改为"Mellow"。我们可以这样实现:

buffer.put(0,(byte)'M').put((byte)'w');

这里通过进行一次绝对方案的 put 将 0 位置的字节代替为ॱ六进制数值 0x4d,将 0x77 放入当前位置(当前位置不会受到绝对 put()的影响)的字节,并将位置属性加一。

修改后的buffer

6. 翻转

我们已经写满了缓冲区,现在我们必须准备将其清空。我们想把这个缓冲区传递给一个通 道,以使内容能被全部写出。但如果通道现在在缓冲区上执行 get(),那么它将从我们 插入的有用数据之外取出未定义数据。

如果我们将位置值重新设为 0,通道就会从正确位置开始获取,但是它是怎样知道何时到达我们所插入数据末端的呢?这就是上界属性被引入的目 的。上界属性指明了缓冲区有效内容的末端。我们需要将上界属性设置为当前位置,然后将位置重置为 0。我们可以人工用下面的代码实现:

buffer.limit(buffer.position()).position(0);

但这种从填充到释放状态的缓冲区翻转是 API 设计者预先设计好的,他们为我们提供了 一个非常便利的函数:java Buffer.flip();
Flip()函数将一个能够继续␫加数据元素的填充状态的缓冲区翻转成一个准备读出元素 的释放状态。

被翻转后的缓冲区

rewind()

Rewind()函数与 flip()相似,但不影响上界属性。它只是将位置值设回 0。您可以使用 rewind()后退,重读已经被翻转的缓冲区中的数据。

如果将缓冲区翻转两次会怎样呢?它实际上会大小变为 0。按照相同步骤对缓冲 区进行操作;把上界设为位置的值,并把位置设为 0。上界和位置都变成 0。尝试对缓冲区上 位置和上界都为 0 的 get()操作会导致 BufferUnderflowException 异常。而 put()则 会导致 BufferOverflowException 异常。

7. 释放

如果您接收到一个在别处被填满的缓冲区, 您可能需要在检索内容之前将其翻转。
如果一个通道的 read()操作完成,而您想要查看被通道放入缓冲区内的数据,那 么您需要在调用 get()之前翻转缓冲区。通道对象在缓冲区上调用 put()增加数据;put 和 read 可以随意␧合使用。

布尔函数 hasRemaining()会在释放缓冲区时告诉您是否已经达到缓冲区的上界。以下是一种将数据元素从缓冲区释放到一个数组的方法(允许多线程同时从缓冲区释 放元素)

for (int i = 0; buffer.hasRemaining(); i++) {
    myByteArray [i] = buffer.get( );
}

作为选择,remaining()函数将告知您从当前位置到上界还剩余的元素数目。您也可以 通过下面的循环来释放的缓冲区。(这种方法会更高效,因为上界不会在每次循环重复时都被检查)

int count = buffer.remaining( ); 
for (int i = 0; i < count, i++) { 
      myByteArray [i] = buffer.get();
}

clear()

一旦缓冲区对象完成填充并释放,它就可以被重新使用了。 Clear()函数将缓冲区重置 为空状态。它并不改变缓冲区中的任何数据元素,而是仅仅将上界设为容量的值,并把位置设 回 0。

填充和释放缓冲区的例子

/**
 * Created by xiaoou on 17/8/17 15:59.
 *
 * @version 1.0
 */
public class BufferFillDrain {
    private static int index = 0;
    private static String [] strings = {
            "A random string value",
            "The product of an infinite number of monkeys",
            "Hey hey we're the Monkees",
            "Opening act for the Monkees: Jimi Hendrix",
            "'Scuse me while I kiss this fly",
            "Help Me! Help Me!",
    };

    public static void main(String[] args) {
        CharBuffer buffer = CharBuffer.allocate(100);

        while (fillBuffer(buffer)) {
            // limit = position, position = 0, mark = -1
            buffer.flip();
            drainBuffer(buffer);
            // position = 0, limit = capacity, mark = -1
            buffer.clear();
        }
    }

    private static void drainBuffer(CharBuffer buffer) {
        while (buffer.hasRemaining()) {
            System.out.print(buffer.get());
        }
        System.out.println();
    }

    private static boolean fillBuffer(CharBuffer buffer) {
        if (index >= strings.length) {
            return false;
        }

        String str = strings[index++];
        for (int i = 0, length = str.length(); i < length; i++) {
            buffer.put(str.charAt(i));
        }

        return true;
    }
}

8. 压缩

public abstract class ByteBuffer extends Buffer implements Comparable {
    // This is a partial API listing
    public abstract ByteBuffer compact();
}

有时,您可能只想从缓冲区中释放一部分数据,而不是全部,然后重新填充。为了实现这 一点,未读的数据元素需要下移以使第一个元素索引为 0。尽管重复这样做会效率低下,但这 有时非常必要,而 API 对此为您提供了一个 compact()函数。这一缓冲区工具在复制数据时 要比您使用 get()和 put()函数高效得多。所以当您需要时,请使用 compact()。

被部分释放的缓冲区

调用 buffer.compact() 之后:
image.png

数据元素 2-5 被复制到 0-3 位置。位置 4 和 5 不受影响, 但现在正在或已经超出了当前位置,因此是"死的"。它们可以被之后的 put()调用重写。 还要注意的是,位置已经被设为被复制的数据元素的数目。也就是说,缓冲区现在被定位在缓 冲区中最后一个Ā存活ā元素后插入数据的位置。最后,上界属性被设置为容量的值,因此缓 冲区可以被再次填满。调用 compact()的作用是ђ弃已经释放的数据,保留未释放的数据, 并使缓冲区对重新填充容量准备就绪。

9. 标记

标记,使缓冲区能够记住一个位置并在之后将其返回。

  1. 缓冲区的标记在 mark( )函数被调用之前是未定义的,
  2. 调用时标记被设为当前位置的值。
  3. reset( )函数将位置设为当前的标记值。如果标记值未定义,调用reset( )将导致 InvalidMarkException 异常。
  4. 一些缓冲区函数会抛弃已经设定的标记 (rewind( ),clear( ),以及 flip( )总是抛弃标记)。
  5. 如果新设定的值比当前的标记小,调用 limit( )或 position( )带有索引参数的版本会抛弃标记。
public final Buffer limit(int newLimit) {
        if ((newLimit > capacity) || (newLimit < 0))
            throw new IllegalArgumentException();
        limit = newLimit;
        if (position > limit) position = limit;
        if (mark > limit) mark = -1;
        return this;
 }
  1. mark()的使用
  buffer.position(2).mark().position(4);
设有一个标记的缓冲区

如果这个缓冲区现在被传递给一个通道,两个字节(ow)将会被发送,而位置会前进到 6。如果我们此时调用 reset( ),位置将会被设为标记。再次将缓冲区传 递给通道将导致四个字节(llow)被发送。


一个缓冲区位置被重置为标记

10. 比较

有时候比较两个缓冲区所包含的数据是很有必要的。 所有的缓冲区都提供了一个常规的 equals( )函数用以测试两个缓冲区的是否相等,以及一个 compareTo( )函数用以比较缓冲区。

public abstract class ByteBuffer extends Buffer implements Comparable { 
    // This is a partial API listing 
    public boolean equals (Object ob) 
    public int compareTo (Object ob) 
}

两个缓冲区可用下面的代码来测试是否相等:

if (buffer1.equals(buffer2)) { doSomething( ); }

如果每个缓冲区中剩余的内容相同,那么 equals( )函数将返回 true,否则返回 false。 因为这个测试是用于严格的相等而且是可换向的。

两个缓冲区被认为相等的充要条件

  • 两个对象类型相同。包含不同数据类型的 buffer 永远不会相等,而且 buffer 绝不会等于非 buffer 对象。
  • 两个对象都剩余同样数量的元素。Buffer 的容量不需要相同,而且缓冲区中剩余数据的索引也不必相同。但每个缓冲区中剩余元素的数目(从位置到上界)必须相同。
  • 在每个缓冲区中应被 Get()函数返回的剩余数据元素序列必须一致。
  1. 两个属性不同的缓冲区也可以相等
  1. 两个相似的缓冲区,可能看起来是完全相同的缓冲区,但测试时会发现并不相等。


compareTo()函数

缓冲区也支持用 compareTo( )函数以䇽典顺序进行比较。 这一函数在缓冲区参数小 于, 等于, 或者大于引用 compareTo( )的对象实例时, 分别返回一个负整数, 0 和正整 数。这些就是所有典型的缓冲区所实现的 java.lang.Comparable 接口语义。这意味着缓 冲区数组可以通过调用 java.util.Arrays.sort()函数按照它们的内容进行排序。

与 equals( )相似,compareTo( )不允许不同对象间进行比较。但 compareTo( )更为严格:如 果您传递一个类型错误的对象,它会抛出 ClassCastException 异常,但 equals( )只会返回 false。

比较是针对每个缓冲区内剩余数据进行的,与它们在 equals( )中的方式相同,直到不相等 的元素被发现或者到达缓冲区的上界。如果一个缓冲区在不相等元素发现前已经被耗尽,较短 的缓冲区被认为是小于较长的缓冲区。不像 equals( ),compareTo( )不可交换:顺序问题。

11. 批量移动

缓冲区的涉及目的就是为了能够高效传输数据。一次移动一个数据元素效率太低

buffer API 提供了向缓冲区内 外ᢩ量移动数据元素的函数

public abstract class CharBuffer extends Buffer implements CharSequence, Comparable {
    // This is a partial API listing
    public CharBuffer get (char [] dst) 
    public CharBuffer get (char [] dst, int offset, int length)

    public final CharBuffer put (char[] src) 
    public CharBuffer put (char [] src, int offset, int length)
    public CharBuffer put (CharBuffer src)

    public final CharBuffer put (String src) 
    public CharBuffer put (String src, int start, int end)
}

有两种形式的 get( )可供从缓冲区到数组进行的数据复制使用。

  1. 第一种形式只将一个数组 作为参数,将一个缓冲区释放到给定的数组。
  2. 第二种形式使用 offset 和 length 参数来指 定目标数组的子区间。
  3. 这些ᢩ量移动的合成效果与前文所讨论的循环是相同的,但是这些方法可能高效得多,因为这种缓冲区实现能够利用本地代码或其他的优化来移动数据。

ᢩ量移动总是具有指定的长度。也就是说,您总是要求移动固定数量的数据元素。当参看 程序签名时这一点还不明显,但是对 get( )的这一引用:

    buffer.get(myArray);

等价于:

  buffer.get(myArray,0,myArray.length);

如果您所要求的数量的数据不能被传送, 那么不会有数据被传递, 缓冲区的状态保持不 变,同时抛出 BufferUnderflowException 异常。因此当您传入一个数组并且没有指定长 度,您就相当于要求整个数组被填充。如果缓冲区中的数据不够完全填满数组,您会得到一个 异常。这意味着如果您想将一个小型缓冲区传入一个大型数组,您需要明确地指定缓冲区中剩 余的数据长度。

  1. 要将一个缓冲区释放到一个大数组中
char [] bigArray = new char [1000]; 
// Get count of chars remaining in the buffer 
int length = buffer.remaining( );
 // Buffer is known to contain < 1,000 chars 
buffer.get (bigArrray, 0, length); 
// Do something useful with the data 
processData (bigArray, length);

记住在调用 get( )之前必须查询缓冲区中的元素数量(因为我们需要告知 processData( )被 放置在 bigArray 中的字符个数)。调用 get( )会向前移动缓冲区的位置属性,所以之后调用 remaining( )会返回 0。get( )的ᢩ量版本返回缓冲区的引用,而不是被传送的数据元素的计数

  1. 缓冲区存有比数组能容纳的数量更多的数据
char [] smallArray = new char [10]; 
while (buffer.hasRemaining( )) {
    int length = Math.min (buffer.remaining(), smallArray.length);
    buffer.get (smallArray, 0, length);
    processData (smallArray, length); 
}
  1. 调用带有一个缓冲区引用作为参数的 put()来在两个缓冲区内进行ᢩ批量传递
buffer.put(srcBuffer);

这等价于(假设 dstBuffer 有足够的空间):

while (srcBuffer.hasRemaining( )) { 
  dstBuffer.put (srcBuffer.get( ));
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,175评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,674评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,151评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,597评论 1 269
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,505评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,969评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,455评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,118评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,227评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,213评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,214评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,928评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,512评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,616评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,848评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,228评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,772评论 2 339

推荐阅读更多精彩内容

  • Java NIO(New IO)是从Java 1.4版本开始引入的一个新的IO API,可以替代标准的Java I...
    JackChen1024阅读 7,520评论 1 143
  • 转自 http://www.ibm.com/developerworks/cn/education/java/j-...
    抓兔子的猫阅读 2,265评论 0 22
  • 我们以Buffer类开始我们对java.nio软件包的浏览历程,这些类是java.nio的构造基础。一个Buffe...
    文武文武阅读 727评论 0 0
  • Buffer java NIO库是在jdk1.4中引入的,NIO与IO之间的第一个区别在于,IO是面向流的,而NI...
    德彪阅读 2,180评论 0 3
  • 本篇文章是基于谷歌有关Graphic的一篇概览文章的翻译:http://source.android.com/de...
    lee_3do阅读 7,073评论 2 21