一改衩、Buffer類簡介
一個Buffer對象是固定數(shù)量的數(shù)據(jù)的容器。其作用是一個存儲器侍筛,或者分段運輸區(qū)萤皂,在這里數(shù)據(jù)可被存儲并在之后用于檢索。
緩沖區(qū)的工作與通道緊密聯(lián)系勾笆。通道是I/O傳輸發(fā)生時通過的入口敌蚜, 而緩沖區(qū)是這些數(shù)據(jù)傳輸?shù)膩碓椿蚰繕恕?/p>
對于離開緩沖區(qū)的傳輸,要傳遞出去的數(shù)據(jù)被置于一個緩沖區(qū)窝爪,被傳送到通道弛车。
對于傳回緩沖區(qū)的傳遞,一個通道將數(shù)據(jù)放置在你所提供的緩沖區(qū)中蒲每。
- 這種在協(xié)同對象(通常是您所寫的對象以及一到多個 Channel 對象)之間進行的緩沖區(qū)數(shù)據(jù)傳遞是高效數(shù)據(jù)處理的關(guān)鍵纷跛。
二、Buffer類層次結(jié)構(gòu)
三邀杏、緩沖區(qū)基礎(chǔ)
- 緩沖區(qū)是包在一個對象內(nèi)的基本數(shù)據(jù)元素數(shù)組
- Buffer 類相比一個簡單數(shù)組的優(yōu)點是它將關(guān)于數(shù)據(jù)的數(shù)據(jù)內(nèi)容和信息包含在一個單一的對象中贫奠。
- 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');
如果我們想在不丟失位置的情況下進行一些 更改該怎么辦呢?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é)翁巍,并將位置屬性加一驴一。
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)。
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ū)能夠記住一個位置并在之后將其返回从藤。
- 緩沖區(qū)的標記在 mark( )函數(shù)被調(diào)用之前是未定義的催跪,
- 調(diào)用時標記被設(shè)為當(dāng)前位置的值。
- reset( )函數(shù)將位置設(shè)為當(dāng)前的標記值夷野。如果標記值未定義懊蒸,調(diào)用reset( )將導(dǎo)致 InvalidMarkException 異常。
- 一些緩沖區(qū)函數(shù)會拋棄已經(jīng)設(shè)定的標記 (rewind( )悯搔,clear( )骑丸,以及 flip( )總是拋棄標記)。
- 如果新設(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;
}
- mark()的使用
buffer.position(2).mark().position(4);
如果這個緩沖區(qū)現(xiàn)在被傳遞給一個通道,兩個字節(jié)(ow)將會被發(fā)送灌曙,而位置會前進到 6菊碟。如果我們此時調(diào)用 reset( ),位置將會被設(shè)為標記平匈。再次將緩沖區(qū)傳 遞給通道將導(dǎo)致四個字節(jié)(llow)被發(fā)送框沟。
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ù)元素序列必須一致膘盖。
- 兩個屬性不同的緩沖區(qū)也可以相等
-
兩個相似的緩沖區(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ù)制使用。
- 第一種形式只將一個數(shù)組 作為參數(shù)分冈,將一個緩沖區(qū)釋放到給定的數(shù)組圾另。
- 第二種形式使用 offset 和 length 參數(shù)來指 定目標數(shù)組的子區(qū)間。
- 這些?量移動的合成效果與前文所討論的循環(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ù)長度腹鹉。
- 要將一個緩沖區(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ù)
- 緩沖區(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);
}
- 調(diào)用帶有一個緩沖區(qū)引用作為參數(shù)的 put()來在兩個緩沖區(qū)內(nèi)進行?批量傳遞
buffer.put(srcBuffer);
這等價于(假設(shè) dstBuffer 有足夠的空間):
while (srcBuffer.hasRemaining( )) {
dstBuffer.put (srcBuffer.get( ));
}