android Buffer的使用

??Buffer的意思就是緩沖區(qū)顽馋,它的作用就是在內(nèi)存中預(yù)留出一定空間的內(nèi)存大小尘喝,主要用來(lái)作為臨時(shí)數(shù)據(jù)的存儲(chǔ)压昼,那么這部分內(nèi)存區(qū)域炕倘,我們就稱(chēng)之為緩沖區(qū)钧大,這樣做的好處有倆個(gè):

??1、減少實(shí)際的物理讀寫(xiě)次數(shù)
??2罩旋、緩沖區(qū)在創(chuàng)建時(shí)就被分配內(nèi)存啊央,這塊內(nèi)存區(qū)域一直被重用,可以減少動(dòng)態(tài)分配和回收內(nèi)存的次數(shù)

??比如涨醋,現(xiàn)在有一批貨需要從A地移到B地瓜饥,有100個(gè)貨物,B地雖然有很大的位置給A的貨物放置浴骂,但是不知道A具體需要放多少東西乓土,那只能一次給一個(gè)貨物的位置,那么A拉貨的時(shí)候就很煩了靠闭,因?yàn)锽告訴A你每次只能拉一個(gè)東西過(guò)來(lái)帐我,因?yàn)槲也恢滥阋嗌贃|西坎炼,我位置不能亂給,那么A就苦逼了拦键,要拉100次谣光,而且每次都要B重新給了貨物的位置,那么最終的結(jié)果就是A和B都很煩芬为,那現(xiàn)在有個(gè)方案了萄金,A告訴B,我現(xiàn)在要50個(gè)貨物的位置媚朦,你給我留著氧敢,我肯定能用上,B說(shuō)询张,好啊孙乖,那你拉吧,這下A開(kāi)心了份氧,一次就拉了50個(gè)貨物過(guò)去唯袄,結(jié)果只要拉倆次就把貨物拉完了,A和B都很開(kāi)心蜗帜。

??那上面的例子就是能夠反映出緩沖區(qū)的作用恋拷,預(yù)留貨物的位置就可以稱(chēng)之為緩沖區(qū)。

??那么再來(lái)看Buffer厅缺,Buffer本身是抽象類(lèi)蔬顾,我們看它的子類(lèi)。
它的子類(lèi)有:

ByteBuffer湘捎,CharBuffer诀豁,ShortBuffer,IntBuffer消痛,LongBuffer且叁,F(xiàn)loatBuffer,DoubleBuffer

??那我們選ByteBuffer看,一看秩伞,我擦逞带,還是抽象的,但是不要緊纱新,ByteBuffer里面的一些方法基本上就可以讓我們知道緩沖區(qū)是如何使用的展氓。
那下面說(shuō)說(shuō)關(guān)于它的一些介紹。

Fields

屬性 描述
Capacity 容量脸爱,即可以容納的最大數(shù)據(jù)量遇汞;在緩沖區(qū)創(chuàng)建時(shí)被設(shè)定并且不能改變
Limit 表示緩沖區(qū)的當(dāng)前終點(diǎn),不能對(duì)緩沖區(qū)超過(guò)極限的位置進(jìn)行讀寫(xiě)操作。且極限是可以修改的
Position 位置空入,下一個(gè)要被讀或?qū)懙脑氐乃饕缢看巫x寫(xiě)緩沖區(qū)數(shù)據(jù)時(shí)都會(huì)改變改值,為下次讀寫(xiě)作準(zhǔn)備
Mark 標(biāo)記歪赢,調(diào)用mark()來(lái)設(shè)置mark=position化戳,再調(diào)用reset()可以讓position恢復(fù)到標(biāo)記的位置

實(shí)例化

??ByteBuffer類(lèi)提供了4個(gè)靜態(tài)工廠方法來(lái)獲得ByteBuffer的實(shí)例:

方法 描述
allocate(int capacity) 從堆空間中分配一個(gè)容量大小為capacity的byte數(shù)組作為緩沖區(qū)的byte數(shù)據(jù)存儲(chǔ)器
allocateDirect(int capacity) 是不使用JVM堆棧而是通過(guò)操作系統(tǒng)來(lái)創(chuàng)建內(nèi)存塊用作緩沖區(qū),它與當(dāng)前操作系統(tǒng)能夠更好的耦合埋凯,因此能進(jìn)一步提高I/O操作速度点楼。但是分配直接緩沖區(qū)的系統(tǒng)開(kāi)銷(xiāo)很大,因此只有在緩沖區(qū)較大并長(zhǎng)期存在白对,或者需要經(jīng)常重用時(shí)掠廓,才使用這種緩沖區(qū)
wrap(byte[] array) 這個(gè)緩沖區(qū)的數(shù)據(jù)會(huì)存放在byte數(shù)組中,bytes數(shù)組或buff緩沖區(qū)任何一方中數(shù)據(jù)的改動(dòng)都會(huì)影響另一方甩恼。其實(shí)ByteBuffer底層本來(lái)就有一個(gè)bytes數(shù)組負(fù)責(zé)來(lái)保存buffer緩沖區(qū)中的數(shù)據(jù)蟀瞧,通過(guò)allocate方法系統(tǒng)會(huì)幫你構(gòu)造一個(gè)byte數(shù)組
wrap(byte[] array,int offset, int length) 在上一個(gè)方法的基礎(chǔ)上可以指定偏移量和長(zhǎng)度,這個(gè)offset也就是包裝后byteBuffer的position媳拴,而length呢就是limit-position的大小黄橘,從而我們可以得到limit的位置為length+position(offset)

Methods

方法 描述
limit(), limit(10)等 其中讀取和設(shè)置這4個(gè)屬性的方法的命名和jQuery中的val(),val(10)類(lèi)似,一個(gè)負(fù)責(zé)get屈溉,一個(gè)負(fù)責(zé)set
reset() 把position設(shè)置成mark的值,相當(dāng)于之前做過(guò)一個(gè)標(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)就是將一個(gè)處于存數(shù)據(jù)狀態(tài)的緩沖區(qū)變?yōu)橐粋€(gè)處于準(zhǔn)備取數(shù)據(jù)的狀態(tài)
rewind() 把position設(shè)為0线梗,mark設(shè)為-1,不改變limit的值
remaining() return limit - position; 返回limit和position之間相對(duì)位置差
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() 相對(duì)讀,從position位置讀取一個(gè)byte抢呆,并將position+1煮嫌,為下次讀寫(xiě)作準(zhǔn)備
get(int index) 絕對(duì)讀,讀取byteBuffer底層的bytes中下標(biāo)為index的byte抱虐,不改變position
get(byte[] dst, int offset, int length) 從position位置開(kāi)始相對(duì)讀昌阿,讀length個(gè)byte,并寫(xiě)入dst下標(biāo)從offset到offset+length的區(qū)域
put(byte b) 相對(duì)寫(xiě),向position的位置寫(xiě)入一個(gè)byte懦冰,并將postion+1灶轰,為下次讀寫(xiě)作準(zhǔn)備
put(int index, byte b) 絕對(duì)寫(xiě),向byteBuffer底層的bytes中下標(biāo)為index的位置插入byte b刷钢,不改變position
put(ByteBuffer src) 用相對(duì)寫(xiě)笋颤,把src中可讀的部分(也就是position到limit)寫(xiě)入此byteBuffer
put(byte[] src, int offset, int length) 從src數(shù)組中的offset到offset+length區(qū)域讀取數(shù)據(jù)并使用相對(duì)寫(xiě)寫(xiě)入此byteBuffer

??那說(shuō)了這么多方法的使用后,我們可以去看一看這個(gè)預(yù)留內(nèi)存到底是怎么安排的闯捎,查看byteBuffer椰弊,我們可以發(fā)現(xiàn)有一個(gè)變量hb,是final byte[] hb,其余的變量都是其余基本數(shù)據(jù)類(lèi)型瓤鼻,應(yīng)該不是我們想要的秉版,那么圍繞這個(gè)查找,在put方法里面我們可以查看這樣一段代碼

if (this.hb != null && src.hb != null) {
      System.arraycopy(src.hb, src.position() + src.offset, hb, position() + offset, n);
} else {
      final Object srcObject = src.isDirect() ? src : src.hb;
      int srcOffset = src.position();
      if (!src.isDirect()) {
           srcOffset += src.offset;
      }
      final ByteBuffer dst = this;
      final Object dstObject = dst.isDirect() ? dst : dst.hb;
      int dstOffset = dst.position();
      if (!dst.isDirect()) {
           dstOffset += dst.offset;
       }
       Memory.memmove(dstObject, dstOffset, srcObject, srcOffset, n);
}

??在這里我們可以看到會(huì)對(duì)hb是否為空進(jìn)行判斷茬祷,如果不為空?qǐng)?zhí)行

System.arraycopy(src.hb, src.position() + src.offset, hb, position() + offset, n);

??如果為空清焕,則執(zhí)行下面長(zhǎng)長(zhǎng)的代碼,一般的代碼不必多說(shuō)祭犯,看這一行代碼

Memory.memmove(dstObject, dstOffset, srcObject, srcOffset, n);

??整個(gè)代碼片段里面我們可以從這倆個(gè)方法秸妥,但是其實(shí)這倆個(gè)方法都是底層實(shí)現(xiàn)的,那這里我們解釋下具體作用是什么沃粗。

System.arraycopy()

??首先說(shuō)個(gè)知識(shí)前提粥惧,Java數(shù)組的復(fù)制操作可以分為深度復(fù)制和淺度復(fù)制,簡(jiǎn)單來(lái)說(shuō)深度復(fù)制最盅,可以將對(duì)象的值和對(duì)象的內(nèi)容復(fù)制;淺復(fù)制是指對(duì)對(duì)象引用的復(fù)制突雪。
System.arraycopy的函數(shù)原型是

public static void arraycopy(Object src,int srcPos,Object dest,int destPos,int length)

??其中,src表示源數(shù)組涡贱,srcPos表示源數(shù)組要復(fù)制的起始位置咏删,desc表示目標(biāo)數(shù)組,length表示要復(fù)制的長(zhǎng)度问词,那么對(duì)于一維數(shù)組來(lái)說(shuō)督函,這種復(fù)制屬性值傳遞,修改副本不會(huì)影響原來(lái)的值激挪。對(duì)于二維或者一維數(shù)組中存放的是對(duì)象時(shí)辰狡,復(fù)制結(jié)果是一維的引用變量傳遞給副本的一維數(shù)組,修改副本時(shí)灌灾,會(huì)影響原來(lái)的數(shù)組搓译。這個(gè)各位看官可以自己去測(cè)試一下,這里就不再舉例了锋喜。

Memory.memmove()

??memcpy和memmove()都是C語(yǔ)言中的庫(kù)函數(shù)些己,在頭文件string.h中豌鸡,作用是拷貝一定長(zhǎng)度的內(nèi)存的內(nèi)容,原型分別如下

void *memcpy(void *dst, const void *src, size_t count);
void *memmove(void *dst, const void *src, size_t count);

??他們的作用是一樣的段标,唯一的區(qū)別是涯冠,當(dāng)內(nèi)存發(fā)生局部重疊的時(shí)候,memmove保證拷貝的結(jié)果是正確的逼庞,memcpy不保證拷貝的結(jié)果的正確蛇更。
我們看看這倆個(gè)方法的具體實(shí)現(xiàn)

void* my_memcpy(void* dst, const void* src, size_t n)
{
    char *tmp = (char*)dst;
    char *s_src = (char*)src;
    while(n--) {
        *tmp++ = *s_src++;
    }
    return dst;
}

??從實(shí)現(xiàn)中可以看出memcpy()是從內(nèi)存左側(cè)一個(gè)字節(jié)一個(gè)字節(jié)地將src中的內(nèi)容拷貝到dest的內(nèi)存中
再看看另一個(gè)方法

void* my_memmove(void* dst, const void* src, size_t n)
{
    char* s_dst;
    char* s_src;
    s_dst = (char*)dst;
    s_src = (char*)src;
    if(s_dst>s_src && (s_src+n>s_dst)) {      //內(nèi)存覆蓋的情形。
        s_dst = s_dst+n-1;
        s_src = s_src+n-1;
        while(n--) {
            *s_dst-- = *s_src--;
        }
    }else {
        while(n--) {
            *s_dst++ = *s_src++;
        }
    }
    return dst;
}

??那么我們?cè)倏纯磧?nèi)存覆蓋是什么情況



??從內(nèi)存可能覆蓋的情況來(lái)看赛糟,當(dāng)使用memcpy的時(shí)候派任,這種實(shí)現(xiàn)方式導(dǎo)致了對(duì)于圖中第二種內(nèi)存重疊情形下,最后兩個(gè)字節(jié)的拷貝值明顯不是原先的值了璧南,新的值是變成了src的最開(kāi)始的2個(gè)字節(jié)了掌逛,而memmove是可以正常工作的。

??到這里關(guān)于Buffer的介紹就結(jié)束了司倚,最后感謝以下文章的提供者豆混。

??ByteBuffer常用方法詳解
??System.arraycopy()方法詳解
??memmove 和 memcpy的區(qū)別以及處理內(nèi)存重疊問(wèn)題

??喜歡技術(shù)研究的可以加入QQ群一起討論:561176094

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市动知,隨后出現(xiàn)的幾起案子皿伺,更是在濱河造成了極大的恐慌,老刑警劉巖盒粮,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鸵鸥,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡丹皱,警方通過(guò)查閱死者的電腦和手機(jī)脂男,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)种呐,“玉大人,你說(shuō)我怎么就攤上這事弃甥∷遥” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵淆攻,是天一觀的道長(zhǎng)阔墩。 經(jīng)常有香客問(wèn)我,道長(zhǎng)瓶珊,這世上最難降的妖魔是什么啸箫? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮伞芹,結(jié)果婚禮上忘苛,老公的妹妹穿的比我還像新娘蝉娜。我一直安慰自己,他們只是感情好扎唾,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開(kāi)白布召川。 她就那樣靜靜地躺著,像睡著了一般胸遇。 火紅的嫁衣襯著肌膚如雪荧呐。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,287評(píng)論 1 301
  • 那天纸镊,我揣著相機(jī)與錄音倍阐,去河邊找鬼。 笑死逗威,一個(gè)胖子當(dāng)著我的面吹牛峰搪,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播庵楷,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼罢艾,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了尽纽?” 一聲冷哼從身側(cè)響起咐蚯,我...
    開(kāi)封第一講書(shū)人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎弄贿,沒(méi)想到半個(gè)月后春锋,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡差凹,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年期奔,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片危尿。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡呐萌,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出谊娇,到底是詐尸還是另有隱情肺孤,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布济欢,位于F島的核電站赠堵,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏法褥。R本人自食惡果不足惜茫叭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望半等。 院中可真熱鬧揍愁,春花似錦呐萨、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至烁登,卻和暖如春怯屉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背饵沧。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工锨络, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人狼牺。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓羡儿,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親是钥。 傳聞我的和親對(duì)象是個(gè)殘疾皇子掠归,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354

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

  • 轉(zhuǎn)自 http://www.ibm.com/developerworks/cn/education/java/j-...
    抓兔子的貓閱讀 2,305評(píng)論 0 22
  • Buffer java NIO庫(kù)是在jdk1.4中引入的,NIO與IO之間的第一個(gè)區(qū)別在于悄泥,IO是面向流的虏冻,而NI...
    德彪閱讀 2,202評(píng)論 0 3
  • # Java NIO # Java NIO屬于非阻塞IO,這是與傳統(tǒng)IO最本質(zhì)的區(qū)別弹囚。傳統(tǒng)IO包括socket和文...
    Teddy_b閱讀 592評(píng)論 0 0
  • 以 Buffer 類(lèi)開(kāi)始我們對(duì) java.nio 軟件包的瀏覽歷程厨相。這些類(lèi)是 java.nio 的構(gòu)造基礎(chǔ)。在本章...
    沉淪2014閱讀 583評(píng)論 0 4
  • 為了提高性能鸥鹉,通常將頂點(diǎn)蛮穿,顏色等值存放在java.nio 包中定義的Buffer類(lèi)中。 例: ByteBuffer...
    長(zhǎng)何閱讀 647評(píng)論 0 0