Java NIO-3.Buffer

Java NIO Buffers用来和NIO Channels交互。正如前文所述,数据从通道中读到缓冲区,或者从缓冲区写到通道。
缓冲区本质上是一块能写入数据,并延迟读取的内存。这块内存被包装成一个NIO Buffer类,并提供了一组方法简化对它的访问。

Buffer的基本用法

对Buffer的读写一般遵循以下四个步骤:

  1. 写入数据到Buffer
  2. 调用buffer.flip()方法
  3. 从Buffer中读取数据
  4. 滴啊用buffer.clear()或者buffer.compact()方法

向缓冲区写入数据时,缓冲区记录写入了多少数据。一旦要读取数据,需要调用flip()方法将缓冲区从写模式转换到读模式。在读模式下,可以读取到之前写入缓冲区的数据。

完成数据读取后,需要清空缓冲区,让它可以再次被写入。有两种方式能够清空缓冲区:调用clear()方法或者compact()方法。clear()方法清空整个缓冲区。compact()方法仅清空已经读取的部分。所有未读取的数据被移动到缓冲区的开头。新数据将从未读取数据的后面开始写入。

以下是一个使用Buffer的简单实例,注意其中的write,flip,read,clear操作(原文中意为这四个操作用黑体标识,但是简书不支持)。

RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw");
FileChannel inChannel = aFile.getChannel();

//create buffer with capacity of 48 bytes
ByteBuffer buf = ByteBuffer.allocate(48);

int bytesRead = inChannel.read(buf); //read into buffer.
while (bytesRead != -1) {

  buf.flip();  //make buffer ready for read

  while(buf.hasRemaining()){
      System.out.print((char) buf.get()); // read 1 byte at a time
  }

  buf.clear(); //make buffer ready for writing
  bytesRead = inChannel.read(buf);
}
aFile.close();

Buffer的Capacity,Position,和Limit

缓冲区本质上是一块可以写入数据,然后从中读取的内存。这块内存被包装成一个NIO Buffer对象,提供了一组方法用来简化对内存的访问。
为了了解Buffer的工作原理,需要熟悉它的三个属性:

  • capacity
  • position
  • limit
    position和limit的含义取决于Buffer处在读膜还是还是写模式。capacity的含义是不变的。
    以下是一个关于capacity,position,limit在读写模式中的插图,详细的解释在插图后面:


    Buffer capacity,postion and limit in write and read mode

Capacity

作为一个内存块,Buffer有固定的大小,叫做"capacity(容量)"。只能往里面写入capacity个bytes,long,chars等类型。一旦Buffer满了,需要清空它才能继续往里面写入数据。

Position

数据写入Buffer中时,需要知道一个确切的位置。初始位置是0,当一个byte,long等数据写入后,position移动到buffer中的下一个插入数据的位置。position最大可为capacity-1.
从buffer读取数据时,也需要从某个特定的位置读。当buffer被从写模式flip成读模式时,position重置为0.当从Buffer中读数据时,就从position开始,然后position往后移动到下一个读的位置。

Limit

在写模式中,Buffer的Limit是对能写入多少数据的限制。写模式中limit等于Buffer的容量(capacity)
当把Buffer 转换到读模式后,limit代表能从buffer中读取多少数据。所以,buffer转换到读模式时,limit被设定为写模式中写入的位置。也就是说,能从buffer中读取的字节数就等于它被写入的字节数(limit被设定为写入的字节数,在写模式中就是position。)

Buffer Types

Java NIO 中有以下Buffer类型:

  • ByteBuffer
  • MappedByteBuffer
  • CharBuffer
  • DoubleBuffer
  • FloatBuffer
  • IntBuffer
  • LongBuffer
  • ShortBuffer
    可以看到,这些Buffer类型代表了不同的数据类型。换句话说,可以通过char,short,int,long,float或者double来操作buffer中的字节。

Buffer的分配

为了获得一个Buffer对象,需要先对它进行分配(allocate),每一个Buffer对象都有allocate()方法来完成这个工作。
例,分配了容量为48字节的ByteBuffer:

ByteBuffer buf = ByteBuffer.allocate(48);

例,分配容量为1024个字符的CharBuffer:

CharBuffer buf = CharBuffer.allocate(1024);

向Buffer中写

向Buffer中写入数据有两种方法:

  1. 从Channel中写到Buffer
  2. 通过缓冲区的put()方法写入数据。
    例,从Channel写到Buffer:
int byteRead = inChannel.read(buf);// read into a buffer

例,通过put方法写入:

buf.put(127)

put方法有很多不同的版本,提供了数据写入buffer的不同方式。例如,在指定的位置写入,或者写入字节数组。更多关于缓冲区的细节可以参考JavaDoc。

flip()

flip()方法将Buffer从写模式转换到读模式。调用flip()会将position重置为0,将limit设置为刚才的position。
换句话说,position现在标记了读的位置,limit标记了有多少字节,字符等被写入到了buffer——也就是有多少字节,字符可以被读取。

从Buffer中读

有两种方法从Buffer中读取数据:

  1. 从缓冲区读取数据到通道。
  2. 用get()方法从缓冲区中读取数据
    例,从缓冲区中读到通道
// read from buffer into channel
int bytesWritten = inChannel.write(buf);

例,用get()方法从Buffer中读:

byte aByte = buf.get();

get()方法有很多版本,提供了从Buffer读取数据的不同方式。例如,从指定的位置读取,或者读取字节数据。更多关于缓冲区的细节可以参考JavaDoc。

rewind()

Buffer.rewind()方法将position重置为0。这样能够重读buffer中数据。limit保持不变,仍然表示能够从buffer中读取的数据量。

clear()和compact()

一旦读取完Buffer中的数据,需要让Buffer做好再次写入的准备。
可以通过调用clear()或者compact()方法实现。
clear()方法会将position重置为0,limit重置为capacity。也就是说,Buffer被清空了。Buffer中的数据并未清除,只有这些标记代表Buffer能被写入数据的位置。
如果Buffer中仍有未被读取的数据,clear()方法将导致它们被遗忘,这意味着不再有任何标记表明那些数据被读取了,哪些没有。
如果Buffer中仍然有未被读取的数据,且后续还需要读取它们,但此时需要先写入数据。就需要使用compact()方法代替clear()方法。
compact()将未读取的数据复制到Buffer 的开头,然后将position设置为最后一个未读元素的后面,limit属性和clear()方法一样,被设置为capacity。之后就可以往Buffer中写入数据了,并且不会覆盖未读数据。

mark()和reset()

可以通过调用Buffer.mark()方法在Buffer中标记一个指定的位置。之后可以通过Buffer.reset()方法恢复到这个位置。
例如:

buffer.mark();
// call buffer.get() a couple of time, e.g. during parsing.
buffer.reset;// set position back to mark

equals() 和compareTo()

使用equals()和compareTo()方法可以比较两个缓冲区。

equals()

满足一下条件,判断两个缓冲区相等:

  • 类型相同(byte,char,int等)
  • 剩余字节,字符等数量相同,
  • 所有剩余的字节,字符相同
    尅建,equals仅仅比较了Buffer中的部分数据,而不是其中的每一个元素,实际上,仅仅比较了Buffer中的剩余数据。

compareTo()

compareTo()方法比较了两个缓冲区中的剩余数据,在例如排序等需求中。如果满足一下条件,一个Buffer被认为小于另一个:

  1. 第一个不相等的元素小于另一个buffer中的对应元素(原文有误)。
  2. 所以的元素相等,但是第一个Buffer比另一个先耗尽(第一个Buffer的元素更少)。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,999评论 4 368
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,102评论 1 302
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,709评论 0 250
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,439评论 0 217
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,846评论 3 294
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,881评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,062评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,783评论 0 205
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,517评论 1 248
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,762评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,241评论 1 264
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,568评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,236评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,145评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,941评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,965评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,802评论 2 275

推荐阅读更多精彩内容