ByteBuffer常用方法详解
緩沖區(qū)(Buffer)就是在內(nèi)存中預(yù)留指定大小的存儲空間用來對輸入/輸出(I/O)的數(shù)據(jù)作臨時存儲,這部分預(yù)留的內(nèi)存空間就叫做緩沖區(qū):
使用緩沖區(qū)有這么兩個好處:
1、減少實(shí)際的物理讀寫次數(shù)
2、緩沖區(qū)在創(chuàng)建時就被分配內(nèi)存,這塊內(nèi)存區(qū)域一直被重用,可以減少動態(tài)分配和回收內(nèi)存的次數(shù)
舉個簡單的例子,比如A地有1w塊磚要搬到B地
由于沒有工具(緩沖區(qū)),我們一次只能搬一本,那么就要搬1w次(實(shí)際讀寫次數(shù))
如果A,B兩地距離很遠(yuǎn)的話(IO性能消耗),那么性能消耗將會很大
但是要是此時我們有輛大卡車(緩沖區(qū)),一次可運(yùn)5000本,那么2次就夠了
相比之前,性能肯定是大大提高了。
而且一般在實(shí)際過程中,我們一般是先將文件讀入內(nèi)存,再從內(nèi)存寫出到別的地方
這樣在輸入輸出過程中我們都可以用緩存來提升IO性能。
所以,buffer在IO中很重要。在舊I/O類庫中(相對java.nio包)中的BufferedInputStream、BufferedOutputStream、BufferedReader和BufferedWriter在其實(shí)現(xiàn)中都運(yùn)用了緩沖區(qū)。java.nio包公開了Buffer API,使得Java程序可以直接控制和運(yùn)用緩沖區(qū)。
在Java NIO中,緩沖區(qū)的作用也是用來臨時存儲數(shù)據(jù),可以理解為是I/O操作中數(shù)據(jù)的中轉(zhuǎn)站。緩沖區(qū)直接為通道(Channel)服務(wù),寫入數(shù)據(jù)到通道或從通道讀取數(shù)據(jù),這樣的操利用緩沖區(qū)數(shù)據(jù)來傳遞就可以達(dá)到對數(shù)據(jù)高效處理的目的。在NIO中主要有八種緩沖區(qū)類(其中MappedByteBuffer是專門用于內(nèi)存映射的一種ByteBuffer):
Fields
?
所有緩沖區(qū)都有4個屬性:capacity、limit、position、mark,并遵循:mark <= position <= limit <= capacity,下表格是對著4個屬性的解釋:
屬性 描述
| Capacity | 容量,即可以容納的最大數(shù)據(jù)量;在緩沖區(qū)創(chuàng)建時被設(shè)定并且不能改變 |
| Limit | 表示緩沖區(qū)的當(dāng)前終點(diǎn),不能對緩沖區(qū)超過極限的位置進(jìn)行讀寫操作。且極限是可以修改的 |
| Position | 位置,下一個要被讀或?qū)懙脑氐乃饕?#xff0c;每次讀寫緩沖區(qū)數(shù)據(jù)時都會改變改值,為下次讀寫作準(zhǔn)備 |
| Mark | 標(biāo)記,調(diào)用mark()來設(shè)置mark=position,再調(diào)用reset()可以讓position恢復(fù)到標(biāo)記的位置 |
Methods
?
1、實(shí)例化
java.nio.Buffer類是一個抽象類,不能被實(shí)例化。Buffer類的直接子類,如ByteBuffer等也是抽象類,所以也不能被實(shí)例化。
但是ByteBuffer類提供了4個靜態(tài)工廠方法來獲得ByteBuffer的實(shí)例:
方法 描述
| allocate(int capacity) | 從堆空間中分配一個容量大小為capacity的byte數(shù)組作為緩沖區(qū)的byte數(shù)據(jù)存儲器 |
| allocateDirect(int capacity) | 是不使用JVM堆棧而是通過操作系統(tǒng)來創(chuàng)建內(nèi)存塊用作緩沖區(qū),它與當(dāng)前操作系統(tǒng)能夠更好的耦合,因此能進(jìn)一步提高I/O操作速度。但是分配直接緩沖區(qū)的系統(tǒng)開銷很大,因此只有在緩沖區(qū)較大并長期存在,或者需要經(jīng)常重用時,才使用這種緩沖區(qū) |
| wrap(byte[] array) | 這個緩沖區(qū)的數(shù)據(jù)會存放在byte數(shù)組中,bytes數(shù)組或buff緩沖區(qū)任何一方中數(shù)據(jù)的改動都會影響另一方。其實(shí)ByteBuffer底層本來就有一個bytes數(shù)組負(fù)責(zé)來保存buffer緩沖區(qū)中的數(shù)據(jù),通過allocate方法系統(tǒng)會幫你構(gòu)造一個byte數(shù)組 |
| wrap(byte[] array, ?int offset, int length) | 在上一個方法的基礎(chǔ)上可以指定偏移量和長度,這個offset也就是包裝后byteBuffer的position,而length呢就是limit-position的大小,從而我們可以得到limit的位置為length+position(offset) |
?
?
我寫了這幾個方法的測試方法,大家可以運(yùn)行起來更容易理解
public static void main(String args[]) throws FileNotFoundException {System.out.println("----------Test allocate--------");System.out.println("before alocate:"+ Runtime.getRuntime().freeMemory());// 如果分配的內(nèi)存過小,調(diào)用Runtime.getRuntime().freeMemory()大小不會變化?// 要超過多少內(nèi)存大小JVM才能感覺到?ByteBuffer buffer = ByteBuffer.allocate(102400);System.out.println("buffer = " + buffer);System.out.println("after alocate:"+ Runtime.getRuntime().freeMemory());// 這部分直接用的系統(tǒng)內(nèi)存,所以對JVM的內(nèi)存沒有影響B(tài)yteBuffer directBuffer = ByteBuffer.allocateDirect(102400);System.out.println("directBuffer = " + directBuffer);System.out.println("after direct alocate:"+ Runtime.getRuntime().freeMemory());System.out.println("----------Test wrap--------");byte[] bytes = new byte[32];buffer = ByteBuffer.wrap(bytes);System.out.println(buffer);buffer = ByteBuffer.wrap(bytes, 10, 10);System.out.println(buffer); }
?
2、另外一些常用的方法
?
方法 描述
| limit(), limit(10)等 | 其中讀取和設(shè)置這4個屬性的方法的命名和jQuery中的val(),val(10)類似,一個負(fù)責(zé)get,一個負(fù)責(zé)set |
| reset() | 把position設(shè)置成mark的值,相當(dāng)于之前做過一個標(biāo)記,現(xiàn)在要退回到之前標(biāo)記的地方 |
| clear() | position = 0;limit = capacity;mark = -1; ?有點(diǎn)初始化的味道,但是并不影響底層byte數(shù)組的內(nèi)容 |
| flip() | limit = position;position = 0;mark = -1; ?翻轉(zhuǎn),也就是讓flip之后的position到limit這塊區(qū)域變成之前的0到position這塊,翻轉(zhuǎn)就是將一個處于存數(shù)據(jù)狀態(tài)的緩沖區(qū)變?yōu)橐粋€處于準(zhǔn)備取數(shù)據(jù)的狀態(tài) |
| rewind() | 把position設(shè)為0,mark設(shè)為-1,不改變limit的值 |
| remaining() | return limit - position;返回limit和position之間相對位置差 |
| hasRemaining() | return position < limit返回是否還有未讀內(nèi)容 |
| compact() | 把從position到limit中的內(nèi)容移到0到limit-position的區(qū)域內(nèi),position和limit的取值也分別變成limit-position、capacity。如果先將positon設(shè)置到limit,再compact,那么相當(dāng)于clear() |
| get() | 相對讀,從position位置讀取一個byte,并將position+1,為下次讀寫作準(zhǔn)備 |
| get(int index) | 絕對讀,讀取byteBuffer底層的bytes中下標(biāo)為index的byte,不改變position |
| get(byte[] dst, int offset, int length) | 從position位置開始相對讀,讀length個byte,并寫入dst下標(biāo)從offset到offset+length的區(qū)域 |
| put(byte b) | 相對寫,向position的位置寫入一個byte,并將postion+1,為下次讀寫作準(zhǔn)備 |
| put(int index, byte b) | 絕對寫,向byteBuffer底層的bytes中下標(biāo)為index的位置插入byte b,不改變position |
| put(ByteBuffer src) | 用相對寫,把src中可讀的部分(也就是position到limit)寫入此byteBuffer |
| put(byte[] src, int offset, int length) | 從src數(shù)組中的offset到offset+length區(qū)域讀取數(shù)據(jù)并使用相對寫寫入此byteBuffer |
?
?
?
以下為一些測試方法:
?
public static void main(String args[]){System.out.println("--------Test reset----------");buffer.clear();buffer.position(5);buffer.mark();buffer.position(10);System.out.println("before reset:" + buffer);buffer.reset();System.out.println("after reset:" + buffer);System.out.println("--------Test rewind--------");buffer.clear();buffer.position(10);buffer.limit(15);System.out.println("before rewind:" + buffer);buffer.rewind();System.out.println("before rewind:" + buffer);System.out.println("--------Test compact--------");buffer.clear();buffer.put("abcd".getBytes());System.out.println("before compact:" + buffer);System.out.println(new String(buffer.array()));buffer.flip();System.out.println("after flip:" + buffer);System.out.println((char) buffer.get());System.out.println((char) buffer.get());System.out.println((char) buffer.get());System.out.println("after three gets:" + buffer);System.out.println("\t" + new String(buffer.array()));buffer.compact();System.out.println("after compact:" + buffer);System.out.println("\t" + new String(buffer.array()));System.out.println("------Test get-------------");buffer = ByteBuffer.allocate(32);buffer.put((byte) 'a').put((byte) 'b').put((byte) 'c').put((byte) 'd').put((byte) 'e').put((byte) 'f');System.out.println("before flip()" + buffer);// 轉(zhuǎn)換為讀取模式buffer.flip();System.out.println("before get():" + buffer);System.out.println((char) buffer.get());System.out.println("after get():" + buffer);// get(index)不影響position的值System.out.println((char) buffer.get(2));System.out.println("after get(index):" + buffer);byte[] dst = new byte[10];buffer.get(dst, 0, 2);System.out.println("after get(dst, 0, 2):" + buffer);System.out.println("\t dst:" + new String(dst));System.out.println("buffer now is:" + buffer);System.out.println("\t" + new String(buffer.array()));System.out.println("--------Test put-------");ByteBuffer bb = ByteBuffer.allocate(32);System.out.println("before put(byte):" + bb);System.out.println("after put(byte):" + bb.put((byte) 'z'));System.out.println("\t" + bb.put(2, (byte) 'c'));// put(2,(byte) 'c')不改變position的位置System.out.println("after put(2,(byte) 'c'):" + bb);System.out.println("\t" + new String(bb.array()));// 這里的buffer是 abcdef[pos=3 lim=6 cap=32]bb.put(buffer);System.out.println("after put(buffer):" + bb);System.out.println("\t" + new String(bb.array()));}
轉(zhuǎn)載于:https://www.cnblogs.com/JAYIT/p/8384476.html
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎總結(jié)
以上是生活随笔為你收集整理的ByteBuffer常用方法详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: .Net TCP/UDP 编程 【一】【
- 下一篇: Egret入门学习日记 --- 第二篇