Java NIO讀書筆記 -- (三) 緩沖區(qū)基礎(chǔ)

一改衩、Buffer類簡介

  1. 一個Buffer對象是固定數(shù)量的數(shù)據(jù)的容器。其作用是一個存儲器侍筛,或者分段運輸區(qū)萤皂,在這里數(shù)據(jù)可被存儲并在之后用于檢索。

  2. 緩沖區(qū)的工作與通道緊密聯(lián)系勾笆。通道是I/O傳輸發(fā)生時通過的入口敌蚜, 而緩沖區(qū)是這些數(shù)據(jù)傳輸?shù)膩碓椿蚰繕恕?/p>

對于離開緩沖區(qū)的傳輸,要傳遞出去的數(shù)據(jù)被置于一個緩沖區(qū)窝爪,被傳送到通道弛车。
對于傳回緩沖區(qū)的傳遞,一個通道將數(shù)據(jù)放置在你所提供的緩沖區(qū)中蒲每。

  1. 這種在協(xié)同對象(通常是您所寫的對象以及一到多個 Channel 對象)之間進行的緩沖區(qū)數(shù)據(jù)傳遞是高效數(shù)據(jù)處理的關(guān)鍵纷跛。

二、Buffer類層次結(jié)構(gòu)

Buffer類層次結(jié)構(gòu)

三邀杏、緩沖區(qū)基礎(chǔ)

  1. 緩沖區(qū)是包在一個對象內(nèi)的基本數(shù)據(jù)元素數(shù)組
  2. Buffer 類相比一個簡單數(shù)組的優(yōu)點是它將關(guān)于數(shù)據(jù)的數(shù)據(jù)內(nèi)容和信息包含在一個單一的對象中贫奠。
  3. Buffer 類以及它專有的子類定義了一個用于處理數(shù)據(jù)緩沖區(qū)的 API。

四望蜡、緩沖區(qū)屬性

所有的緩沖區(qū)都具有四個屬性來提供關(guān)于其所包含的數(shù)據(jù)元素的信息唤崭。

1.容量(Capacity)

緩沖區(qū)能夠容納的數(shù)據(jù)元素的最大數(shù)量。這一容量在緩沖區(qū)創(chuàng)建時被設(shè)定脖律,并且永遠不能被改變

2.上界(Limit)

緩沖區(qū)的第一個不能被讀或?qū)懙脑匦簧觥;蛘哒f小泉,緩沖區(qū)中現(xiàn)存元素的計數(shù)芦疏。

3.位置(Position)

下一個要被讀或?qū)懙脑氐乃饕N恢脮詣佑上鄳?yīng)的get()和put()函數(shù)更新微姊。

4.標記(Mark)

一個備忘位置酸茴。調(diào)用mark()來設(shè)定mark=position。調(diào)用reset()設(shè)定position = mark兢交。標記在設(shè)定前是未定義的(Undefined)

這四個屬性之間的關(guān)系:

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

五薪捍、新創(chuàng)建ByteBuffer示例

位置被設(shè)為 0,而且容量和上界被設(shè)為 10配喳,?好經(jīng)過緩沖區(qū)能夠容納的最后一個字節(jié)飘诗。 標記最初未定義。容量是固定的界逛,但另外的三個屬性可以在使用緩沖區(qū)時改變昆稿。

六、緩沖區(qū)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鏈式調(diào)用

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

可以被簡寫為:

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

3.isReadOnly()函數(shù)

所有的緩沖區(qū)都是可讀的息拜,但并非所有都可寫溉潭。
每個具體的緩沖區(qū)類都通過執(zhí)行 isReadOnly()來標示其是否允許該緩存區(qū)的內(nèi)容被修改净响。

4.存取

緩沖區(qū)管理著固定數(shù)目的數(shù)據(jù)元素。但在任何特定的時刻喳瓣,我們可能 只對緩沖區(qū)中的一部分元素感興趣馋贤。換句話說,在我們想清空緩沖區(qū)之前畏陕,我們可能只使用了 緩沖區(qū)的一部分配乓。

我們需要能夠追蹤添加到緩沖區(qū)內(nèi)的數(shù)據(jù)元素的數(shù)量,放入下一個元 素的位置等等的方法惠毁。位置屬性做到了這一點犹芹。

它在調(diào)用 put()時指出了下一個數(shù)據(jù)元素應(yīng) 該被插入的位置,或者當(dāng) get()被調(diào)用時指出下一個元素應(yīng)從何處檢索鞠绰。

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');
五次調(diào)用put()之后的緩沖區(qū)

如果我們想在不丟失位置的情況下進行一些 更改該怎么辦呢?put()的絕對方案可以達到這樣的目的蜈膨。 假設(shè)我們想將緩沖區(qū)中的內(nèi)容從 “Hello"的 ASCII 碼更改為"Mellow"屿笼。我們可以這樣實現(xiàn):

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

這里通過進行一次絕對方案的 put 將 0 位置的字節(jié)代替為?六進制數(shù)值 0x4d,將 0x77 放入當(dāng)前位置(當(dāng)前位置不會受到絕對 put()的影響)的字節(jié)翁巍,并將位置屬性加一驴一。

修改后的buffer

6. 翻轉(zhuǎn)

我們已經(jīng)寫滿了緩沖區(qū),現(xiàn)在我們必須準備將其清空灶壶。我們想把這個緩沖區(qū)傳遞給一個通 道肝断,以使內(nèi)容能被全部寫出。但如果通道現(xiàn)在在緩沖區(qū)上執(zhí)行 get()例朱,那么它將從我們 插入的有用數(shù)據(jù)之外取出未定義數(shù)據(jù)孝情。

如果我們將位置值重新設(shè)為 0鱼蝉,通道就會從正確位置開始獲取洒嗤,但是它是怎樣知道何時到達我們所插入數(shù)據(jù)末端的呢?這就是上界屬性被引入的目 的魁亦。上界屬性指明了緩沖區(qū)有效內(nèi)容的末端渔隶。我們需要將上界屬性設(shè)置為當(dāng)前位置,然后將位置重置為 0洁奈。我們可以人工用下面的代碼實現(xiàn):

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

但這種從填充到釋放狀態(tài)的緩沖區(qū)翻轉(zhuǎn)是 API 設(shè)計者預(yù)先設(shè)計好的间唉,他們?yōu)槲覀兲峁┝?一個非常便利的函數(shù):java Buffer.flip();
Flip()函數(shù)將一個能夠繼續(xù)?加數(shù)據(jù)元素的填充狀態(tài)的緩沖區(qū)翻轉(zhuǎn)成一個準備讀出元素 的釋放狀態(tài)。

被翻轉(zhuǎn)后的緩沖區(qū)

rewind()

Rewind()函數(shù)與 flip()相似利术,但不影響上界屬性呈野。它只是將位置值設(shè)回 0。您可以使用 rewind()后退印叁,重讀已經(jīng)被翻轉(zhuǎn)的緩沖區(qū)中的數(shù)據(jù)被冒。

如果將緩沖區(qū)翻轉(zhuǎn)兩次會怎樣呢军掂?它實際上會大小變?yōu)?0。按照相同步驟對緩沖 區(qū)進行操作昨悼;把上界設(shè)為位置的值蝗锥,并把位置設(shè)為 0。上界和位置都變成 0率触。嘗試對緩沖區(qū)上 位置和上界都為 0 的 get()操作會導(dǎo)致 BufferUnderflowException 異常终议。而 put()則 會導(dǎo)致 BufferOverflowException 異常。

7. 釋放

如果您接收到一個在別處被填滿的緩沖區(qū)葱蝗, 您可能需要在檢索內(nèi)容之前將其翻轉(zhuǎn)穴张。
如果一個通道的 read()操作完成,而您想要查看被通道放入緩沖區(qū)內(nèi)的數(shù)據(jù)垒玲,那 么您需要在調(diào)用 get()之前翻轉(zhuǎn)緩沖區(qū)陆馁。通道對象在緩沖區(qū)上調(diào)用 put()增加數(shù)據(jù);put 和 read 可以隨意?合使用合愈。

布爾函數(shù) hasRemaining()會在釋放緩沖區(qū)時告訴您是否已經(jīng)達到緩沖區(qū)的上界叮贩。以下是一種將數(shù)據(jù)元素從緩沖區(qū)釋放到一個數(shù)組的方法(允許多線程同時從緩沖區(qū)釋 放元素)

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

作為選擇,remaining()函數(shù)將告知您從當(dāng)前位置到上界還剩余的元素數(shù)目佛析。您也可以 通過下面的循環(huán)來釋放的緩沖區(qū)益老。(這種方法會更高效,因為上界不會在每次循環(huán)重復(fù)時都被檢查)

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

clear()

一旦緩沖區(qū)對象完成填充并釋放寸莫,它就可以被重新使用了捺萌。 Clear()函數(shù)將緩沖區(qū)重置 為空狀態(tài)。它并不改變緩沖區(qū)中的任何數(shù)據(jù)元素膘茎,而是僅僅將上界設(shè)為容量的值桃纯,并把位置設(shè) 回 0。

填充和釋放緩沖區(qū)的例子

/**
 * 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();
}

有時披坏,您可能只想從緩沖區(qū)中釋放一部分數(shù)據(jù)态坦,而不是全部,然后重新填充棒拂。為了實現(xiàn)這 一點伞梯,未讀的數(shù)據(jù)元素需要下移以使第一個元素索引為 0。盡管重復(fù)這樣做會效率低下帚屉,但這 有時非常必要谜诫,而 API 對此為您提供了一個 compact()函數(shù)。這一緩沖區(qū)工具在復(fù)制數(shù)據(jù)時 要比您使用 get()和 put()函數(shù)高效得多攻旦。所以當(dāng)您需要時喻旷,請使用 compact()。

被部分釋放的緩沖區(qū)

調(diào)用 buffer.compact() 之后:
image.png

數(shù)據(jù)元素 2-5 被復(fù)制到 0-3 位置牢屋。位置 4 和 5 不受影響且预, 但現(xiàn)在正在或已經(jīng)超出了當(dāng)前位置牺陶,因此是"死的"。它們可以被之后的 put()調(diào)用重寫辣之。 還要注意的是掰伸,位置已經(jīng)被設(shè)為被復(fù)制的數(shù)據(jù)元素的數(shù)目。也就是說怀估,緩沖區(qū)現(xiàn)在被定位在緩 沖區(qū)中最后一個ā存活ā元素后插入數(shù)據(jù)的位置狮鸭。最后,上界屬性被設(shè)置為容量的值多搀,因此緩 沖區(qū)可以被再次填滿歧蕉。調(diào)用 compact()的作用是?棄已經(jīng)釋放的數(shù)據(jù),保留未釋放的數(shù)據(jù)康铭, 并使緩沖區(qū)對重新填充容量準備就緒惯退。

9. 標記

標記,使緩沖區(qū)能夠記住一個位置并在之后將其返回从藤。

  1. 緩沖區(qū)的標記在 mark( )函數(shù)被調(diào)用之前是未定義的催跪,
  2. 調(diào)用時標記被設(shè)為當(dāng)前位置的值。
  3. reset( )函數(shù)將位置設(shè)為當(dāng)前的標記值夷野。如果標記值未定義懊蒸,調(diào)用reset( )將導(dǎo)致 InvalidMarkException 異常。
  4. 一些緩沖區(qū)函數(shù)會拋棄已經(jīng)設(shè)定的標記 (rewind( )悯搔,clear( )骑丸,以及 flip( )總是拋棄標記)。
  5. 如果新設(shè)定的值比當(dāng)前的標記小妒貌,調(diào)用 limit( )或 position( )帶有索引參數(shù)的版本會拋棄標記通危。
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);
設(shè)有一個標記的緩沖區(qū)

如果這個緩沖區(qū)現(xiàn)在被傳遞給一個通道,兩個字節(jié)(ow)將會被發(fā)送灌曙,而位置會前進到 6菊碟。如果我們此時調(diào)用 reset( ),位置將會被設(shè)為標記平匈。再次將緩沖區(qū)傳 遞給通道將導(dǎo)致四個字節(jié)(llow)被發(fā)送框沟。


一個緩沖區(qū)位置被重置為標記

10. 比較

有時候比較兩個緩沖區(qū)所包含的數(shù)據(jù)是很有必要的藏古。 所有的緩沖區(qū)都提供了一個常規(guī)的 equals( )函數(shù)用以測試兩個緩沖區(qū)的是否相等增炭,以及一個 compareTo( )函數(shù)用以比較緩沖區(qū)。

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

兩個緩沖區(qū)可用下面的代碼來測試是否相等:

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

如果每個緩沖區(qū)中剩余的內(nèi)容相同拧晕,那么 equals( )函數(shù)將返回 true隙姿,否則返回 false。 因為這個測試是用于嚴格的相等而且是可換向的厂捞。

兩個緩沖區(qū)被認為相等的充要條件

  • 兩個對象類型相同输玷。包含不同數(shù)據(jù)類型的 buffer 永遠不會相等队丝,而且 buffer 絕不會等于非 buffer 對象。
  • 兩個對象都剩余同樣數(shù)量的元素欲鹏。Buffer 的容量不需要相同机久,而且緩沖區(qū)中剩余數(shù)據(jù)的索引也不必相同。但每個緩沖區(qū)中剩余元素的數(shù)目(從位置到上界)必須相同赔嚎。
  • 在每個緩沖區(qū)中應(yīng)被 Get()函數(shù)返回的剩余數(shù)據(jù)元素序列必須一致膘盖。
  1. 兩個屬性不同的緩沖區(qū)也可以相等
  1. 兩個相似的緩沖區(qū),可能看起來是完全相同的緩沖區(qū)尤误,但測試時會發(fā)現(xiàn)并不相等侠畔。


compareTo()函數(shù)

緩沖區(qū)也支持用 compareTo( )函數(shù)以?典順序進行比較。 這一函數(shù)在緩沖區(qū)參數(shù)小 于损晤, 等于软棺, 或者大于引用 compareTo( )的對象實例時, 分別返回一個負整數(shù)尤勋, 0 和正整 數(shù)喘落。這些就是所有典型的緩沖區(qū)所實現(xiàn)的 java.lang.Comparable 接口語義。這意味著緩 沖區(qū)數(shù)組可以通過調(diào)用 java.util.Arrays.sort()函數(shù)按照它們的內(nèi)容進行排序最冰。

與 equals( )相似揖盘,compareTo( )不允許不同對象間進行比較。但 compareTo( )更為嚴格:如 果您傳遞一個類型錯誤的對象锌奴,它會拋出 ClassCastException 異常兽狭,但 equals( )只會返回 false。

比較是針對每個緩沖區(qū)內(nèi)剩余數(shù)據(jù)進行的鹿蜀,與它們在 equals( )中的方式相同箕慧,直到不相等 的元素被發(fā)現(xiàn)或者到達緩沖區(qū)的上界。如果一個緩沖區(qū)在不相等元素發(fā)現(xiàn)前已經(jīng)被耗盡茴恰,較短 的緩沖區(qū)被認為是小于較長的緩沖區(qū)颠焦。不像 equals( ),compareTo( )不可交換:順序問題往枣。

11. 批量移動

緩沖區(qū)的涉及目的就是為了能夠高效傳輸數(shù)據(jù)伐庭。一次移動一個數(shù)據(jù)元素效率太低

buffer API 提供了向緩沖區(qū)內(nèi) 外?量移動數(shù)據(jù)元素的函數(shù)

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( )可供從緩沖區(qū)到數(shù)組進行的數(shù)據(jù)復(fù)制使用。

  1. 第一種形式只將一個數(shù)組 作為參數(shù)分冈,將一個緩沖區(qū)釋放到給定的數(shù)組圾另。
  2. 第二種形式使用 offset 和 length 參數(shù)來指 定目標數(shù)組的子區(qū)間。
  3. 這些?量移動的合成效果與前文所討論的循環(huán)是相同的雕沉,但是這些方法可能高效得多集乔,因為這種緩沖區(qū)實現(xiàn)能夠利用本地代碼或其他的優(yōu)化來移動數(shù)據(jù)。

?量移動總是具有指定的長度坡椒。也就是說扰路,您總是要求移動固定數(shù)量的數(shù)據(jù)元素尤溜。當(dāng)參看 程序簽名時這一點還不明顯,但是對 get( )的這一引用:

    buffer.get(myArray);

等價于:

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

如果您所要求的數(shù)量的數(shù)據(jù)不能被傳送汗唱, 那么不會有數(shù)據(jù)被傳遞宫莱, 緩沖區(qū)的狀態(tài)保持不 變,同時拋出 BufferUnderflowException 異常哩罪。因此當(dāng)您傳入一個數(shù)組并且沒有指定長 度梢睛,您就相當(dāng)于要求整個數(shù)組被填充。如果緩沖區(qū)中的數(shù)據(jù)不夠完全填滿數(shù)組识椰,您會得到一個 異常绝葡。這意味著如果您想將一個小型緩沖區(qū)傳入一個大型數(shù)組,您需要明確地指定緩沖區(qū)中剩 余的數(shù)據(jù)長度腹鹉。

  1. 要將一個緩沖區(qū)釋放到一個大數(shù)組中
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);

記住在調(diào)用 get( )之前必須查詢緩沖區(qū)中的元素數(shù)量(因為我們需要告知 processData( )被 放置在 bigArray 中的字符個數(shù))藏畅。調(diào)用 get( )會向前移動緩沖區(qū)的位置屬性,所以之后調(diào)用 remaining( )會返回 0功咒。get( )的?量版本返回緩沖區(qū)的引用愉阎,而不是被傳送的數(shù)據(jù)元素的計數(shù)

  1. 緩沖區(qū)存有比數(shù)組能容納的數(shù)量更多的數(shù)據(jù)
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. 調(diào)用帶有一個緩沖區(qū)引用作為參數(shù)的 put()來在兩個緩沖區(qū)內(nèi)進行?批量傳遞
buffer.put(srcBuffer);

這等價于(假設(shè) dstBuffer 有足夠的空間):

while (srcBuffer.hasRemaining( )) { 
  dstBuffer.put (srcBuffer.get( ));
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市力奋,隨后出現(xiàn)的幾起案子榜旦,更是在濱河造成了極大的恐慌,老刑警劉巖景殷,帶你破解...
    沈念sama閱讀 222,627評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件溅呢,死亡現(xiàn)場離奇詭異,居然都是意外死亡猿挚,警方通過查閱死者的電腦和手機咐旧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來绩蜻,“玉大人铣墨,你說我怎么就攤上這事“炀” “怎么了伊约?”我有些...
    開封第一講書人閱讀 169,346評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長孕蝉。 經(jīng)常有香客問我屡律,道長,這世上最難降的妖魔是什么昔驱? 我笑而不...
    開封第一講書人閱讀 60,097評論 1 300
  • 正文 為了忘掉前任疹尾,我火速辦了婚禮上忍,結(jié)果婚禮上骤肛,老公的妹妹穿的比我還像新娘纳本。我一直安慰自己,他們只是感情好腋颠,可當(dāng)我...
    茶點故事閱讀 69,100評論 6 398
  • 文/花漫 我一把揭開白布繁成。 她就那樣靜靜地躺著,像睡著了一般淑玫。 火紅的嫁衣襯著肌膚如雪巾腕。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,696評論 1 312
  • 那天絮蒿,我揣著相機與錄音尊搬,去河邊找鬼。 笑死土涝,一個胖子當(dāng)著我的面吹牛佛寿,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播但壮,決...
    沈念sama閱讀 41,165評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼冀泻,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蜡饵?” 一聲冷哼從身側(cè)響起弹渔,我...
    開封第一講書人閱讀 40,108評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎溯祸,沒想到半個月后肢专,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,646評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡焦辅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,709評論 3 342
  • 正文 我和宋清朗相戀三年鸟召,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片氨鹏。...
    茶點故事閱讀 40,861評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡欧募,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出仆抵,到底是詐尸還是另有隱情跟继,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布镣丑,位于F島的核電站舔糖,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏莺匠。R本人自食惡果不足惜金吗,卻給世界環(huán)境...
    茶點故事閱讀 42,196評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧摇庙,春花似錦旱物、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,698評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至夕凝,卻和暖如春宝穗,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背码秉。 一陣腳步聲響...
    開封第一講書人閱讀 33,804評論 1 274
  • 我被黑心中介騙來泰國打工逮矛, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人转砖。 一個月前我還...
    沈念sama閱讀 49,287評論 3 379
  • 正文 我出身青樓橱鹏,卻偏偏與公主長得像,于是被迫代替她去往敵國和親堪藐。 傳聞我的和親對象是個殘疾皇子莉兰,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,860評論 2 361

推薦閱讀更多精彩內(nèi)容

  • Java NIO(New IO)是從Java 1.4版本開始引入的一個新的IO API,可以替代標準的Java I...
    JackChen1024閱讀 7,557評論 1 143
  • 轉(zhuǎn)自 http://www.ibm.com/developerworks/cn/education/java/j-...
    抓兔子的貓閱讀 2,313評論 0 22
  • 我們以Buffer類開始我們對java.nio軟件包的瀏覽歷程礁竞,這些類是java.nio的構(gòu)造基礎(chǔ)糖荒。一個Buffe...
    文武文武閱讀 754評論 0 0
  • Buffer java NIO庫是在jdk1.4中引入的,NIO與IO之間的第一個區(qū)別在于模捂,IO是面向流的捶朵,而NI...
    德彪閱讀 2,211評論 0 3
  • 本篇文章是基于谷歌有關(guān)Graphic的一篇概覽文章的翻譯:http://source.android.com/de...
    lee_3do閱讀 7,140評論 2 21