Java-NIO(3)
ByteBuffer
ByteBuffer爲Buffer子類,可以在緩衝區中以字節爲單位對數據進行存取
此類定義了對字節緩衝區的六種操作類別:
-
讀取和寫入單個字節的絕對和相對的get和put方法;
-
相對批量獲取方法,用於將字節的連續序列從此緩衝區傳輸到數組中;
-
相對批量放置方法,用於將字節數組或某些其他字節緩衝區中連續的字節序列傳輸到此緩衝區中;
-
絕對和相對的get和put方法,用於讀取和寫入其他基本類型的值,並將它們以特定字節順序在字節序列之間來回轉換;
-
創建視圖緩衝區的方法,該方法允許將字節緩衝區視爲包含某些其他原始類型值的緩衝區;
-
一種壓縮字節緩衝區的方法。
創建堆緩衝區與字節緩衝區
字節緩衝區分兩種:
-
直接字節緩衝區
-
非直接字節緩衝區
針對直接字節緩衝區:
JVM儘量在直接字節緩衝區上執行IO操作,直接對內核空間進行訪問,提高運行效率
allocateDirect與allocate
源碼:
//新緩衝區的位置將爲零,其極限將是其容量,其標記將是未定義的,其每個元素將被初始化爲零,並且其字節順序將爲BIG_ENDIAN。 不確定是否具有支持數組。
public static ByteBuffer allocateDirect(int capacity) {
return new DirectByteBuffer(capacity);
}
//新緩衝區的位置將爲零,其極限將是其容量,其標記將是未定義的,其每個元素將被初始化爲零,並且其字節順序將爲BIG_ENDIAN。 它將有一個支持數組,其數組偏移量將爲零。
public static ByteBuffer allocate(int capacity) {
if (capacity < 0)
throw new IllegalArgumentException();
return new HeapByteBuffer(capacity, capacity);
}
對比下allocate方法與wrap方法發現:
-
前者是創建一個新的數組,而後者則是使用傳入的數組作爲存儲空間
-
wrap()關聯的數組進行操作會影響到緩衝區中的數據,而操作緩衝區中數據也會影響到與之關聯的數組中數據,因爲它們引用的是用一個數組對象
public class T1 {
public static void main(String[] args) {
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(100);
ByteBuffer byteBuffe2 = ByteBuffer.allocate(100);
print(byteBuffer);
System.out.println("-----------------------------------");
print(byteBuffe2);
}
private static void print(Buffer buffer) {
System.out.println("position : \n"
+ buffer.position()
+"\n"
+"limit : \n"
+buffer.limit()
+"\n"
+buffer
+"isDirect?\n"
+buffer.isDirect());
}
}
直接與非直接緩衝區運行效率比較
public class T2 {
public static void main(String[] args) {
// t1();//973
// t2();//1307
}
private static void t1(){
long begin = System.currentTimeMillis();
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1000000000);
for (int i = 0; i < 1000000000; i++) {
byteBuffer.put((byte) 123);
}
long end = System.currentTimeMillis();
System.out.println(end - begin);
}
private static void t2(){
long begin = System.currentTimeMillis();
ByteBuffer byteBuffer = ByteBuffer.allocate(1000000000);
for (int i = 0; i < 1000000000; i++) {
byteBuffer.put((byte) 123);
}
long end = System.currentTimeMillis();
System.out.println(end - begin);
}
}
wrap數據的處理
wrap(byte[] array) & wrap(byte[] array,int offset,int length)
源碼實現:
//新緩衝區將由給定的字節數組支持; 也就是說,對緩衝區的修改將導致數組被修改,反之亦然。 新緩衝區的容量和限制將爲array.length,其位置爲零,其標記爲未定義,其字節順序爲BIG_ENDIAN。 它的支持數組將是給定的數組,其數組偏移量將爲零。
public static ByteBuffer wrap(byte[] array) {
return wrap(array, 0, array.length);
}
//新緩衝區將由給定的字節數組支持; 也就是說,對緩衝區的修改將導致數組被修改,反之亦然。 新緩衝區的容量爲array.length,其位置爲offset,其限制爲offset + length,其標記爲undefined,其字節順序爲BIG_ENDIAN。 它的支持數組將是給定的數組,其數組偏移量將爲零。
//Parameters:
//array - 將支持新緩衝區的數組
//offset - 要使用的子數組的偏移量; 必須爲非負數,且不得大於array.length。 新緩衝區的位置將設置爲此值。
//length -length-要使用的子數組的長度; 必須爲非負且不大於array.length-offset。 新緩衝區的限制將設置爲偏移量+長度。
public static ByteBuffer wrap(byte[] array,
int offset, int length)
{
try {
return new HeapByteBuffer(array, offset, length);
} catch (IllegalArgumentException x) {
throw new IndexOutOfBoundsException();
}
}
public class T3 {
public static void main(String[] args) {
byte[] bytes = new byte[]{1,2,3,4,5,6,7,8,9};
ByteBuffer b1 = ByteBuffer.wrap(bytes);
ByteBuffer b2 = ByteBuffer.wrap(bytes,2,4);
System.out.println("b1 capacity : \n"
+b1.capacity()
+"\n"
+"b1 limit : \n"
+b1.limit()
+"\n"
+"b1 position : \n"
+b1.position());
System.out.println("");
System.out.println("b2 capacity : \n"
+b2.capacity()
+"\n"
+"b2 limit : \n"
+b2.limit()
+"\n"
+"b2 positon : \n"
+b2.position());
}
}
put() & get()
Buffer類每個子類都定義了兩種get & put操作
-
相對位置操作
-
絕對位置操作
相對位置操作:讀取或寫入元素時,從“當前位置開始”,然後將位置增加所傳輸的元素數量
絕對位置操作:採取顯式的元素索引,操作不影響位置
//使用相對位置
public abstract ByteBuffer put(byte b);
public class T4 {
public static void main(String[] args) {
ByteBuffer b1 = ByteBuffer.allocate(10);
print(b1,"初始");
System.out.println("");
b1.put((byte) 225);
print(b1,"put之後");
System.out.println("");
b1.put((byte) 127);
print(b1,"再次put之後");
b1.rewind();
print(b1,"rewind之後");
System.out.println("");
System.out.println(b1.get());
print(b1,"get 之後");
System.out.println("");
System.out.println(b1.get());
System.out.println("");
print(b1,"再次get之後");
}
private static void print(Buffer buffer,String name) {
System.out.println(name
+"\n"
+"capacity : \n"
+buffer.capacity()
+"\n"
+"limit : \n"
+buffer.limit()
+"\n"
+"position : \n"
+buffer.position());
}
}
執行相對位置讀寫之後,位置呈現遞增狀態,位置自動移動到下一個位置上
put(byte[] src,int offset,int length) & get(byte[] dst,int offset,int length)
源碼實現:
//相對批量放置方法(可選操作)。
//此方法將字節從給定的源數組傳輸到此緩衝區中。 如果要從數組複製的字節多於該緩衝區中剩餘的字節,即,如果length> missing(),則不傳輸任何字節,並拋出BufferOverflowException。
//否則,此方法從給定數組的長度字節複製到此緩衝區,從數組中的給定偏移量開始,並從該緩衝區的當前位置開始。 然後,該緩衝區的位置將增加長度。
//換句話說,以dst.put(src,off,len)形式調用此方法具有與循環完全相同的效果。
//for (int i = off; i < off + len; i++)
//dst.put(a[i]);
//除了首先檢查此緩衝區中是否有足夠的空間,而且它的效率可能要高得多。
//Parameters:
//src-從中讀取字節的數組
//offset-要讀取的第一個字節在數組中的偏移量; 必須爲非負且不大於array.length
//length-要從給定數組中讀取的字節數; 必須爲非負且不大於array.length-offset
public ByteBuffer put(byte[] src, int offset, int length) {
checkBounds(offset, length, src.length);
if (length > remaining())
throw new BufferOverflowException();
int end = offset + length;
for (int i = offset; i < end; i++)
this.put(src[i]);
return this;
}
public class T {
public static void main(String[] args) {
byte[] bytes = {1,2,3,4,5,6,7,8,9};
byte[] bytes2 = {11,22,33,44,55,66,77};
ByteBuffer b1 = ByteBuffer.allocate(10);
b1.put(bytes);
System.out.println(b1.position());
b1.position(2);
b1.put(bytes2,1,3);
System.out.println("A");
byte[] getB = b1.array();
for (int i = 0; i < getB.length ; i++) {
System.out.print(getB[i] + " ");
}
System.out.println("");
System.out.println(b1.position());
System.out.println("");
b1.position(1);
byte[] out = new byte[b1.capacity()];
b1.get(out,3,4);
System.out.println("B");
for (int i = 0; i < out.length; i++) {
System.out.print(out[i] + " ");
}
}
}
圖示:
public static void main(String[] args) {
byte[] bytes = {1,2,3,4,5,6,7,8,9};
ByteBuffer byteBuffer = ByteBuffer.allocate(10);
int index = 0;
while ( index < bytes.length) {
int readLen = Math.min(
byteBuffer.remaining(),
bytes.length - index
);
byteBuffer.put(bytes,index,readLen);
byteBuffer.flip();
byte[] getB = byteBuffer.array();
for (int i = 0; i < byteBuffer.limit(); i++) {
System.out.print(getB[i] + " ");
}
index = index + readLen;
System.out.println("");
byteBuffer.clear();
}
}
上面代碼在byte[]的length大於或等於緩衝區的remaining(),或者小於等於remaining()時都可以正確的運行
put(byte[] src) & get(byte[] dst)
文檔說明:
//相對批量放置方法(可選操作)。
//此方法將給定源字節數組的全部內容傳輸到此緩衝區中。 以dst.put(a)形式調用此方法的行爲與調用的行爲完全相同
// dst.put(a, 0, a.length)
final ByteBuffer put(byte[] src)
//相對批量獲取方法。
//此方法將字節從此緩衝區傳輸到給定的目標數組。 形式爲src.get(a)的此方法的調用與該調用的行爲完全相同
// src.get(a, 0, a.length)
ByteBuffer get(byte[] dst)
public class T2 {
public static void main(String[] args) {
byte[] bytes = new byte[]{1,2,3,4,5};
ByteBuffer byteBuffer = ByteBuffer.allocate(10);
byteBuffer.put((byte) 1);
byteBuffer.put((byte) 2);
System.out.println("position : \n" + byteBuffer.position() + "\n");
byteBuffer.put(bytes);
System.out.println("position : \n" + byteBuffer.position() + "\n");
byteBuffer.flip();
byteBuffer.position(3);
System.out.println("position : \n" + byteBuffer.position() + "\n");
byte[] newB= new byte[byteBuffer.remaining()];
byteBuffer.get(newB);
for (int i = 0; i < newB.length; i++) {
System.out.print(newB[i] + " ");
}
}
}
put(int index,byte b) & get(int index)
文檔說明:
//絕對放置方法(可選操作)。
//將給定字節以給定索引寫入此緩衝區。
abstract ByteBuffer put(int index,byte b)
//絕對獲取方法。 讀取給定索引處的字節。
abstract byte get(int index)
這兩個操作都屬於絕對位置操作,位置position不會發生改變
public class T3 {
public static void main(String[] args) {
// test1();
test2();
}
private static void test1() {
byte[] bytes = {1,2,3,4,5};
ByteBuffer buffer = ByteBuffer.allocate(10);
buffer.put(bytes);
buffer.put(2, (byte) 123);
System.out.println(buffer.get(2));
buffer.position(0);
byte[] bytes1 = new byte[buffer.capacity()];
buffer.get(bytes1,0,bytes1.length);
for (int i = 0; i < bytes1.length; i++) {
System.out.print(bytes1[i] + " ");
}
}
private static void test2() {
ByteBuffer byteBuffer = ByteBuffer.allocate(10);
byteBuffer.position(9);
System.out.println(byteBuffer.position());
byteBuffer.put(2, (byte) 127);
System.out.println(byteBuffer.position());
byteBuffer.rewind();
byte[] out = new byte[byteBuffer.capacity()];
byteBuffer.get(out,0,out.length);
for (int i = 0; i < out.length; i++) {
System.out.print(out[i] + " " );
}
}
}
put(ByteBuffer src)
JDK:
//相對批量放置方法(可選操作)。
//此方法將給定源緩衝區中剩餘的字節傳輸到此緩衝區中。 如果源緩衝區中剩餘的字節數大於此緩衝區中的字節數,即if src.remaining() > remaining(),則不傳輸任何字節,並拋出BufferOverflowException。
//否則,此方法從每個緩衝區的當前位置開始,將n = src.remaining()字節從給定緩衝區複製到此緩衝區中。 然後將兩個緩衝區的位置加n。
//換句話說,以dst.put(src)形式調用此方法具有與循環完全相同的效果。
// while (src.hasRemaining())
//dst.put(src.get());
//除了首先檢查此緩衝區中是否有足夠的空間,而且它的效率可能要高得多。
public ByteBuffer put(ByteBuffer src) {
if (src == this)
throw new IllegalArgumentException();
if (isReadOnly())
throw new ReadOnlyBufferException();
int n = src.remaining();
if (n > remaining())
throw new BufferOverflowException();
for (int i = 0; i < n; i++)
put(src.get());
return this;
}
public class T4 {
public static void main(String[] args) {
byte[] bs = {1,2,3,4,5,6,7,8};
ByteBuffer b1 = ByteBuffer.wrap(bs);
byte[] bs2 = {55,66,77};
ByteBuffer b2 = ByteBuffer.wrap(bs2);
b1.position(4);
b2.position(1);
b1.put(b2);
System.out.println("b1改變 " +b1.position() + "\n");
System.out.println("b2改變 " +b2.position() + "\n");
byte[] out = b1.array();
for (int i = 0; i < out.length; i++) {
System.out.print(out[i] + " ");
}
}
}
putType() & getType()
JDK:
//讀取char值的相對get方法。
//讀取此緩衝區當前位置的下兩個字節,根據當前字節順序將它們組成一個char值,然後將該位置加2。
abstract char getChar()
//用於寫入char值的相對put方法(可選操作)。
//以當前字節順序將包含給定char值的兩個字節寫入當前位置的此緩衝區中,然後將該位置加2。
abstract ByteBuffer putChar(char value)
//用於讀取char值的絕對get方法。
//讀取給定索引處的兩個字節,根據當前字節順序將它們組成一個char值。
abstract char getChar(int index)
//用於寫入char值的絕對put方法(可選操作)。
//將包含當前char值的給定char值的兩個字節按給定索引寫入此緩衝區。
abstract ByteBuffer putChar(int index,char value)
其餘基本類型操作與上面類似
public class T5 {
public static void main(String[] args) {
ByteBuffer b1 = ByteBuffer.allocate(100);
b1.putChar('a'); // 0 - 1 char 佔兩個字節
b1.putChar(2,'b'); // 2-3
b1.position(4);
b1.putDouble(1.1);// 4 - 11 double 佔8個字節
b1.putDouble(12,1.2);//12 - 19
b1.position(20);
b1.putFloat(2.1F);//20 - 23 float佔4個字節
b1.putFloat(24,2.6F);
b1.position(28);
b1.putInt(25);// 28-31 int 4 個字節
b1.putInt(32,32);//32-35
b1.position(36);
b1.putLong(41L);//36-43 long 8 字節
b1.putLong(44,45L);
b1.position(52);
b1.putShort((short) 51);// 52 -53 short 2 字節
b1.putShort(54, (short) 56);// 54 -55
b1.position(0);
byte[] out = b1.array();
for (int i = 0; i < out.length; i++) {
System.out.print(out[i] + " ");
}
System.out.println("");
System.out.println(b1.getChar());
System.out.println(b1.getChar(2));
}
}
slice() & arrayOffSet()
JDK:
//創建一個新的字節緩衝區,其內容是該緩衝區內容的共享子序列。
//新緩衝區的內容將從該緩衝區的當前位置開始。 對該緩衝區內容的更改將在新緩衝區中可見,反之亦然; 兩個緩衝區的位置,限制和標記值將是獨立的。
//新緩衝區的位置將爲零,其容量和限制將爲該緩衝區中剩餘的字節數,其標記將爲未定義,其字節順序爲BIG_ENDIAN。 當且僅當該緩衝區是直接緩衝區時,新緩衝區纔是直接緩衝區;當且僅當該緩衝區是隻讀緩衝區時,新緩衝區纔是只讀緩衝區。
//Returns:
//The new byte buffer
public abstract ByteBuffer slice()
//返回此緩衝區的第一個元素在此緩衝區的後備數組內的偏移量(可選操作)。
//如果此緩衝區由數組支持,則緩衝區位置p對應於數組索引p + arrayOffset()。
//在調用此方法之前,請先調用hasArray方法,以確保此緩衝區具有可訪問的後備數組。
//返回值:
//該緩衝區的數組中第一個元素的偏移量
public final int arrayOffset()
public class T6 {
public static void main(String[] args) {
byte[] bytes1 = {1,2,3,4,5,6,7,8};
ByteBuffer buffer1 = ByteBuffer.wrap(bytes1);
buffer1.position(5);
ByteBuffer buffer2 = buffer1.slice();
System.out.println("bu1 position = "
+ buffer1.position()
+"\n"
+"bu1 capacity "
+buffer1.capacity()
+"\n"
+"bu1 limit "
+ buffer1.limit());
System.out.println("---------------------------------");
System.out.println("bu2 position = "
+ buffer2.position()
+"\n"
+"bu2 capacity "
+buffer2.capacity()
+"\n"
+"bu2 limit "
+ buffer2.limit());
System.out.println("");
buffer2.put(0, (byte) 11);
byte[] bs1 = buffer1.array();
byte[] bs2 = buffer2.array();
for (int i = 0; i < bs1.length; i++) {
System.out.print(bs1[i] + " ");
}
System.out.println("");
for (int i = 0; i < bs2.length; i++) {
System.out.print(bs2[i] + " ");
}
}
}
轉換爲CharBuffer字符緩衝區 & 中文處理
JDK:
//創建此字節緩衝區的視圖作爲char緩衝區。
//新緩衝區的內容將從該緩衝區的當前位置開始。 對該緩衝區內容的更改將在新緩衝區中可見,反之亦然; 兩個緩衝區的位置,限制和標記值將是獨立的。
//新緩衝區的位置將爲零,其容量和限制將是該緩衝區中剩餘的字節數除以2,其標記將是未定義的,並且其字節順序將是視圖出現時的字節緩衝區的順序。 創建。 當且僅當該緩衝區是直接緩衝區時,新緩衝區纔是直接緩衝區;當且僅當該緩衝區是隻讀緩衝區時,新緩衝區纔是只讀緩衝區。
public abstract CharBuffer asCharBuffer()
public class T7 {
public static void main(String[] args) {
byte[] bs1 = "我是中國人".getBytes();
// 運行本代碼的*.java文件是UTF-8編碼,所以運行環境取得的編碼默認是UTF-8
System.out.println(Charset.defaultCharset().name());
System.out.println("");
ByteBuffer b1 = ByteBuffer.wrap(bs1);
System.out.println("bytebuffer = " + b1.getClass().getName());
System.out.println("");
CharBuffer charBuffer = b1.asCharBuffer();
System.out.println("charbuffer " + charBuffer.getClass().getName());
System.out.println();
System.out.println("b1 position "
+ b1.position()
+"\n"
+"b1 capacity "
+ b1.capacity()
+"\n"
+"b1 limit "
+b1.limit());
System.out.println("");
System.out.println("c1 position "
+ charBuffer.position()
+"\n"
+"c1 capacity "
+ charBuffer.capacity()
+"\n"
+"c1 limit "
+charBuffer.limit());
System.out.println("");
System.out.println(charBuffer.capacity());
charBuffer.position(0);
for (int i = 0; i < charBuffer.capacity(); i++) {
// get()方法時使用的編碼 Wie UTF-16BE
// 所以亂碼了
System.out.print(charBuffer.get() + " ");
}
}
}
上面代碼中的四個步驟:
-
byte[] bs1 = “我是中國人”.getBytes();將中文轉成字節數組,數組中存儲的編碼爲UTF-8
-
ByteBuffer b1 = ByteBuffer.wrap(bs1);將UTF-8編碼的字節數組轉換成字節緩衝區,緩衝區中存儲的編碼也爲UTF-8
-
CharBuffer charBuffer = b1.asCharBuffer();將編碼格式爲UTF-8的ByteBuffer中的內容轉換成UTF-8編碼的CharBuffer
-
調用CharBuffer子類java.nio.ByteBufferAsCharBufferB中get方法,以UTF-16BE的編碼格式獲得中文時出現編碼不匹配情況,所以亂碼了
解決方案;
public class T8 {
public static void main(String[] args) throws UnsupportedEncodingException {
byte[] bs1 = "我是中國人".getBytes("utf-16BE");
System.out.println(Charset.defaultCharset().name());
System.out.println("");
ByteBuffer b1 = ByteBuffer.wrap(bs1);
System.out.println("bytebuffer = " + b1.getClass().getName());
System.out.println("");
CharBuffer charBuffer = b1.asCharBuffer();
System.out.println("charbuffer " + charBuffer.getClass().getName());
System.out.println();
System.out.println("b1 position "
+ b1.position()
+"\n"
+"b1 capacity "
+ b1.capacity()
+"\n"
+"b1 limit "
+b1.limit());
System.out.println("");
System.out.println("c1 position "
+ charBuffer.position()
+"\n"
+"c1 capacity "
+ charBuffer.capacity()
+"\n"
+"c1 limit "
+charBuffer.limit());
System.out.println("");
System.out.println(charBuffer.capacity());
charBuffer.position(0);
for (int i = 0; i < charBuffer.capacity(); i++) {
System.out.print(charBuffer.get() + " ");
}
}
}
另一種方案;
public class T9 {
public static void main(String[] args) throws UnsupportedEncodingException {
byte[] bs1 = "我是中國人".getBytes("utf-8");
System.out.println(Charset.defaultCharset().name());
ByteBuffer buffer = ByteBuffer.wrap(bs1);
CharBuffer charBuffer = Charset.forName("utf-8").decode(buffer);
charBuffer.position(0);
for (int i = 0; i < charBuffer.limit(); i++) {
System.out.print(charBuffer.get() + " ");
}
}
}
對於ByteBuffer的更改會直接影響到CharBuffer:
public class T11 {
public static void main(String[] args) {
byte[] bytes = "abcdeff".getBytes();
ByteBuffer buffer = ByteBuffer.wrap(bytes);
CharBuffer charBuffer = buffer.asCharBuffer();
for (int i = 0; i < charBuffer.limit(); i++) {
System.out.print(charBuffer.get() + " " );
}
System.out.println("");
buffer.put((byte) 2);
buffer.put((byte) 45);
charBuffer.clear();
for (int i = 0; i < charBuffer.limit(); i++) {
System.out.print(charBuffer.get() + " " );
}
}
}
設置&獲得字節順序
JDK:
//檢索此緩衝區的字節順序。
//在讀取或寫入多字節值以及創建作爲此字節緩衝區視圖的緩衝區時,將使用字節順序。 新創建的字節緩衝區的順序始終爲BIG_ENDIAN。
public final ByteOrder order()
public static final ByteOrder BIG_ENDIAN:表示big-endian字節順序的常數。 按照此順序,多字節值的字節按從最高有效到最低有效的順序排列。
public static final ByteOrder LITTLE_ENDIAN:表示little-endian字節順序的常數。 按此順序,多字節值的字節從最低有效到最高有效。
public class T12 {
public static void main(String[] args) {
int val = 123456789;
ByteBuffer buffer1 = ByteBuffer.allocate(4);
System.out.println(buffer1.order() + " ");
System.out.println(buffer1.order() + " " );
buffer1.putInt(val);
byte[] bs = buffer1.array();
for (int i = 0; i < bs.length; i++) {
System.out.print(bs[i] + " ");
}
System.out.println("");
buffer1 = ByteBuffer.allocate(4);
System.out.println(buffer1.order() + " ");
buffer1.order(ByteOrder.BIG_ENDIAN);
System.out.println(buffer1.order() + " ");
buffer1.putInt(val);
bs = buffer1.array();
for (int i = 0; i < bs.length; i++) {
System.out.print(bs[i] + " ");
}
System.out.println("");
buffer1 = ByteBuffer.allocate(4);
System.out.println(buffer1.order() + " ");
buffer1.order(ByteOrder.LITTLE_ENDIAN);
System.out.println(buffer1.order() + " ");
buffer1.putInt(val);
bs = buffer1.array();
for (int i = 0; i < bs.length; i++) {
System.out.print(bs[i] + " ");
}
}
}
創建只讀緩衝區
JDK:
//創建一個共享該緩衝區內容的新的只讀字節緩衝區。
//新緩衝區的內容就是該緩衝區的內容。 對該緩衝區內容的更改將在新緩衝區中可見。 但是,新緩衝區本身將是隻讀的,並且不允許修改共享內容。 這兩個緩衝區的位置,限制和標記值將是獨立的。
//新緩衝區的容量,限制,位置和標記值將與此緩衝區的容量,限制,位置和標記值相同,並且其字節順序爲BIG_ENDIAN。
//如果此緩衝區本身是隻讀的,則此方法的行爲與重複方法完全相同。
public abstract ByteBuffer asReadOnlyBuffer()
public class T13 {
public static void main(String[] args) {
byte[] bs = {1,2,3,4,5,6,7,8};
ByteBuffer buffer1 = ByteBuffer.wrap(bs);
ByteBuffer read_buffer = buffer1.asReadOnlyBuffer();
System.out.println("is readOnly \n"
+ buffer1.isReadOnly());
System.out.println("is readOnly \n"
+ read_buffer.isReadOnly());
read_buffer.rewind();
read_buffer.put((byte) 123);
}
}
壓縮緩衝區
JDK:
//壓縮此緩衝區(可選操作)。
//緩衝區當前位置與其限制之間的字節(如果有)被複制到緩衝區的開頭。 也就是說,將索引p = position()的字節複製到索引零,將索引p + 1的字節複製到索引1,依此類推,直到索引limit()-1的字節複製到索引n = limit()-1-頁 然後將緩衝區的位置設置爲n + 1,並將其限制設置爲其容量。 如果定義了該標記,則將其丟棄。
//緩衝區的位置設置爲要複製的字節數,而不是零,因此,在調用此方法之後,可以立即調用另一個相對的put方法。
//從緩衝區寫入數據後,如果寫入未完成,請調用此方法。 例如,以下循環通過緩衝區buf將字節從一個通道複製到另一個通道:
// buf.clear(); // Prepare buffer for use
// while (in.read(buf) >= 0 || buf.position != 0) {
// buf.flip();
// out.write(buf);
// buf.compact(); // In case of partial write
// }
public abstract ByteBuffer compact()
圖示:
public class T14 {
public static void main(String[] args) {
ByteBuffer buffer1 = ByteBuffer.wrap(new byte[]{1,2,34,5,6});
System.out.println("capacity : \n"
+ buffer1.capacity()
+"\n"
+"position: \b"
+buffer1.position()
+"\n"
+"limit : \n"
+buffer1.limit());
System.out.println("------------------------------");
System.out.println("getval : \n" + buffer1.get());
System.out.println("------------------------------");
System.out.println("capacity : \n"
+ buffer1.capacity()
+"\n"
+"position: \b"
+buffer1.position()
+"\n"
+"limit : \n"
+buffer1.limit());
System.out.println("------------------------------");
System.out.println("getVal : \n" + buffer1.get());
System.out.println("------------------------------");
buffer1.compact();
System.out.println("compact");
System.out.println("capacity : \n"
+ buffer1.capacity()
+"\n"
+"position: \b"
+buffer1.position()
+"\n"
+"limit : \n"
+buffer1.limit());
byte[] out = buffer1.array();
for (int i = 0; i < out.length ; i++) {
System.out.print(out[i] + " ");
}
}
}
比較緩衝區內容
equals() & compareTo()
JDK:
//區分此緩衝區是否等於另一個對象。
//當且僅當兩個字節緩衝區相等時滿足以下條件:
//它們具有相同的元素類型,
//它們具有相同數量的剩餘元素,並且
//獨立於其起始位置考慮的其餘元素的兩個序列在點上相等。
public boolean equals(Object ob) {
if (this == ob)
return true;
if (!(ob instanceof ByteBuffer))
return false;
ByteBuffer that = (ByteBuffer)ob;
if (this.remaining() != that.remaining())
return false;
int p = this.position();
for (int i = this.limit() - 1, j = that.limit() - 1; i >= p; i--, j--)
if (!equals(this.get(i), that.get(j)))
return false;
return true;
}
通過源碼,可以看出兩個緩衝區的capacity可以不一樣,說明equals比較的是position以及limit的內容是否一致
public class T1 {
public static void main(String[] args) {
// test1();
// test2();
// test3();
test4();
}
/*
判斷是不是自身,如果是 返回true
*/
private static void test2() {
byte[] bs = {1,2,3,4,5};
ByteBuffer buffer = ByteBuffer.wrap(bs);
System.out.println(buffer.equals(buffer));
}
/*
驗證是不是ByteBuffer類的實例 不是返回false
*/
private static void test1() {
byte[] bs = {1,2,3,4,5};
int[] ints = {1,2,3,4,5};
ByteBuffer buffer1 = ByteBuffer.wrap(bs);
IntBuffer buffer2 = IntBuffer.wrap(ints);
System.out.println(buffer1.equals(buffer2));
}
/*
判斷remainging是否一樣 不一樣返回false
*/
private static void test3() {
byte[] b1 = {3,4,5};
byte[] b2 = {1,2,3,4,5,6,7,8};
ByteBuffer buffer1 = ByteBuffer.wrap(b1);
ByteBuffer buffer2 = ByteBuffer.wrap(b2);
buffer1.position(0);
buffer2.position(3);
System.out.println(buffer1.equals(buffer2));
System.out.println("");
System.out.println(buffer1.remaining());
System.out.println(buffer2.remaining());
}
/*
驗證 判斷兩個緩衝區中的 position以及limit之間的數據是否完全一樣
只要有一個字節不一樣 就返回false
*/
private static void test4() {
byte[] b1 = {3,4,5};
byte[] b2 = {1,2,3,4,5,6,7,8};
ByteBuffer buffer1 = ByteBuffer.wrap(b1);
ByteBuffer buffer2 = ByteBuffer.wrap(b2);
buffer1.position(0);
buffer1.limit(3);
buffer2.position(2);
buffer2.limit(5);
System.out.println(buffer1.equals(buffer2));
System.out.println("");
System.out.println(buffer1.remaining());
System.out.println(buffer2.remaining());
System.out.println("");
buffer2.put(3, (byte) 44);
System.out.println("");
System.out.println(buffer1.equals(buffer2));
System.out.println(buffer1.remaining());
System.out.println(buffer2.remaining());
}
}
JDK:
//將此緩衝區與另一個緩衝區進行比較。
//通過按字典順序比較兩個字節緩衝區的其餘元素序列來比較兩個字節緩衝區,而不考慮每個序列在其相應緩衝區中的開始位置。 對字節元素進行比較,就好像通過調用Byte.compare(byte,byte)一樣。
//字節緩衝區無法與任何其他類型的對象進行比較。
public int compareTo(ByteBuffer that) {
int n = this.position() + Math.min(this.remaining(), that.remaining());
for (int i = this.position(), j = that.position(); i < n; i++, j++) {
int cmp = compare(this.get(i), that.get(j));
if (cmp != 0)
return cmp;
}
return this.remaining() - that.remaining();
}
public class T2 {
public static void main(String[] args) {
// test1();//-3
test2();//-5
}
/*
驗證 如果開始與結束的範圍之間有一個字節不一樣 則返回兩者的減數
*/
private static void test1() {
byte[] b1 = {3,4,5};
byte[] b2 = {1,2,3,4,5,6,7,8};
ByteBuffer buffer1 = ByteBuffer.wrap(b1);
ByteBuffer buffer2 = ByteBuffer.wrap(b2);
buffer1.position(0);
buffer2.position(2);
System.out.println(buffer1.compareTo(buffer2));
}
/*
驗證 如果開始與結束之間每個字節一樣 則返回兩者remaining的減數
*/
private static void test2() {
byte[] b1 = {3,4,5};
byte[] b2 = {1,2,3,4,5,6,7,8,9,10};
ByteBuffer buffer1 = ByteBuffer.wrap(b1);
ByteBuffer buffer2 = ByteBuffer.wrap(b2);
buffer1.position(0);
buffer2.position(2);
System.out.println(buffer1.compareTo(buffer2));
}
}
複製緩衝區
JDK:
//創建一個共享該緩衝區內容的新字節緩衝區。
//新緩衝區的內容就是該緩衝區的內容。 對該緩衝區內容的更改將在新緩衝區中可見,反之亦然; 兩個緩衝區的位置,限制和標記值將是獨立的。
//新緩衝區的容量,限制,位置和標記值將與此緩衝區的容量,限制,位置和標記值相同,並且其字節順序爲BIG_ENDIAN。 當且僅當該緩衝區是直接緩衝區時,新緩衝區纔是直接緩衝區;當且僅當該緩衝區是隻讀緩衝區時,新緩衝區纔是只讀緩衝區。
public abstract ByteBuffer duplicate()
public class T3 {
public static void main(String[] args) {
byte[] bs = {1,2,3,4,5};
ByteBuffer buffer1 = ByteBuffer.wrap(bs);
buffer1.position(2);
System.out.println(" 1 catacity limit position \n");
System.out.println(buffer1.capacity()+" " + buffer1.limit()+" "
+buffer1.position());
System.out.println("");
ByteBuffer buffer2 = buffer1.slice();
ByteBuffer buffer3 = buffer1.duplicate();
ByteBuffer buffer4 = buffer1;
System.out.println("2 catacity limit position \n");
System.out.println(buffer2.capacity()+" " + buffer2.limit()+" "
+buffer2.position());
System.out.println("");
System.out.println("3 catacity limit position \n");
System.out.println(buffer3.capacity()+" " + buffer3.limit()+" "
+buffer3.position());
System.out.println("");
buffer2.position(0);
for (int i = buffer2.position(); i < buffer2.limit(); i++) {
System.out.print(buffer2.getChar(i) + " ");
}
System.out.println("");
buffer3.position(3);
for (int i = buffer3.position(); i < buffer3.limit(); i++) {
System.out.print(buffer3.getChar(i) + " ");
}
}
}
緩衝區擴容
一旦創建緩衝區,容量就不能被改變了,如果想對緩衝區進行擴展,就要進行相應的處理
public class T4 {
public static void main(String[] args) {
byte[] bs = {1,2,3,4,5};
ByteBuffer buffer = ByteBuffer.wrap(bs);
ByteBuffer buffer12 = extendsSize(buffer,2);
byte[] out = buffer12.array();
for (int i = 0; i < out.length; i++) {
System.out.print(out[i] + " ");
}
}
public static ByteBuffer extendsSize(ByteBuffer byteBuffer,int extendsSize) {
ByteBuffer newbuffer = ByteBuffer.allocate(byteBuffer.capacity() + extendsSize);
newbuffer.put(byteBuffer);
return newbuffer;
}
}
參考資料
《NIO與Socket編程技術指南》