一、关于缓冲区的四个属性:capacity,position和limit在读写模式中的说明:
1、capacity
缓冲区能够容纳的数据元素的最大数量。这一容量在缓冲区创建时被设定,并且永远不能被改变。一旦Buffer满了,需要将其清空(通过读数据或者清除数据)才能继续往里写数据。
2、position
①当你写数据到Buffer中时,position表示当前的位置。初始的position值为0。每当一个数据写到Buffer后, position都会向前移动到下一个可插入数据的Buffer单元。position最大可为capacity – 1.
②当读取数据时,也是从某个特定位置读。
当将Buffer从写模式切换到读模式,position会被重置为0. 当从Buffer的position处读取数据时,position向前移动到下一个可读的位置。
3、limit
①在写模式下,Buffer的limit表示你最多能往Buffer里写多少数据。写模式下,limit等于Buffer的capacity。
②当切换Buffer到读模式时,limit表示你最多能读到多少数据。因此当切换到读模式时,limit会被设置成写模式下的position值。
二、缓冲区Buffer类介绍:
1、buffer.flip(); //翻转缓冲区
当缓冲去写满后,我们需要把缓冲区的数据传递给一个通道,以使内容能被全部写出。但如果通道直接在缓冲区上执行get()操作, 这是中错误的操作,因为它将从我们刚刚插入的有用数据之外取出未定义数据。因此我们在获取缓冲区数据之前需要调用flip()方法,将position定位到0位置;并且将limit设置成之前写模式下的position的值,用来指明指明了缓冲区有效内容的末端。
2、buffer.clear(); //清除整个缓冲区
方法并不改变缓冲区中的数据,它只是将position设置为0,并将limit设置为等于capacity,从而使缓冲区准备好从缓冲区的put操作或信道的读操作接收新的数据
3、compact() //压缩此缓冲区(可选操作)。
compact()方法只会清除已经读过的数据。任何未读的数据都被移到缓冲区的起始处。将position设置为未读数据的后面位置,因此新写入的数据将放到缓冲区未读数据的后面。limit属性被设置为capacity的值,因此缓冲区可以被再次填满。
三、创建缓冲区:
1、allocate()
ByteBuffer buffer=ByteBuffer.allocate(256);
创建一个容量为256字节的ByteBuffer,如果发现创建的缓冲区容量太小,唯一的选择就是重新创建一个大小合适的缓冲区.
2、wrap()
ByteBuffer buffer=ByteBuffer.wrap(byteArray);
//通过包装一个已有的数组来创建,通过此方法创建的缓冲区保留了被包装数组内的数据。
这段代码构造了一个新的缓冲区对象,但数据元素会存在于数组中。这意味着通过调用put()方法造成的对缓冲区的改动会直接影响这个数组,而且对这个数组的任何改动也会对这个缓冲区对象可见。
上面两种创建缓冲区的区别:
分配操作创建一个缓冲区对象并分配一个私有的空间来储存容量大小的数据元素。
包装操作创建一个缓冲区对象但是不分配任何空间来储存数据元素。它使用您所提供的数组作为存储空间来储存缓冲区中的数据元素。
四、缓冲区的释放:
hasRemaining()会在释放缓冲区时告诉您是否已经达到缓冲区的上界。以下是一种将数据元素从缓冲区释放到一个数组的方法
for (int i = 0; buffer.hasRemaining( ), i++) {
myByteArray [i] = buffer.get( );
}
remaining()方法将告知您从当前位置到上界还剩余的元素数目。您也可以通过下面的循环来释放缓冲区。
int count = buffer.remaining( );
for (int i = 0; i < count, i++) {
myByteArray [i] = buffer.get( );
}
五、向缓冲区批量移动数据的高效方法:
get()的批量操作:
public CharBuffer get (char [] dst)
public CharBuffer get (char [] dst, int offset, int length)
put()的批量操作:
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( )可供从缓冲区到数组进行的数据复制使用:
第一种形式只将一个数组作为参数,将一个缓冲区释放到给定的数组。
第二种形式使用offset和length参数来指定目标数组的子区间。
如果缓冲区中的数据不够完全填满数组,您会得到一个异常。这意味着如果您想将一个小型缓冲区传入一个大型数组,您需要明确地指定缓冲区中剩余的数据长度。要将一个缓冲区释放到一个大数组中,要这样做:
int length = buffer.remaining( );
buffer.get (bigArrray, 0, length);
当然,还有另外一种情况,那就是缓冲区的数据容量大于数组能容纳的数据容量,可以用下面解决办法:
char [] smallArray = new char [10];
while (buffer.hasRemaining( )) {
int length = Math.min (buffer.remaining( ), smallArray.length);
buffer.get (smallArray, 0, length);
processData (smallArray, length); // Do something useful with the data
}
Put()的批量操作:
如果缓冲区有足够的空间接受数组中的数据(buffer.remaining()>myArray.length),数据将会被复制到从当前位置开始的缓冲区,并且缓冲区位置会被提前所增加数据元素的数量。
如果缓冲区中没有足够的空间,那么不会有数据被传递,同时抛出一个BufferOverflowException异常。
六、复制缓冲区:
public abstract CharBuffer duplicate( );
public abstract CharBuffer asReadOnlyBuffer( );
public abstract CharBuffer slice( );
1、duplicate()方法创建了一个与原始缓冲区相似的新缓冲区。两个缓冲区共享数据元素,拥有同样的容量,但每个缓冲区拥有各自的位置,上界和标记属性。对一个缓冲区内的数据元素所做的改变会反映在另外一个缓冲区上。这一副本缓冲区具有与原始缓冲区同样的数据视图。如果原始的缓冲区为只读,或者为直接缓冲区,新的缓冲区将继承这些属性。
2、asReadOnlyBuffer()方法来生成一个只读的缓冲区视图。这与duplicate()相同,除了这个新的缓冲区不允许使用put(),并且其isReadOnly()方法将会返回true。对这一只读缓冲区的put()方法的调用尝试会导致抛出ReadOnlyBufferException异常。
3、分割缓冲区与复制相似,但slice()创建一个从原始缓冲区的当前位置开始的新缓冲区,并且其容量是原始缓冲区的剩余元素数量(limit-position)。这个新缓冲区与原始缓冲区共享一段数据元素子序列。分割出来的缓冲区也会继承只读和直接属性。