3. doSth();
4.}
◇批量移动
缓冲区的设计目的就是为了能够高效传输数据,一次移动一个数据元素并不高效。
如你在下面的程序清单中所看到的那样,bufferAPI提供了向缓冲区你外批量移动数据元素的函数:
Java代码
1.publicabstractclassByteBufferextendsBufferimplementsComparable{
2.publicByteBufferget(byte[]dst);
3.publicByteBufferget(byte[]dst,intoffset,intlength);
4.publicfinalByteBufferput(byte[]src);
5.publicByteBufferput(byte[]src,intoffset,intlength);
6.}
如你在上面的程序清单中所看到的那样,bufferAPI提供了向缓冲区内外批量移动数据元素的函数。
以get为例,它将缓冲区中的内容复制到指定的数组中,当然是从position开始咯。
第二种形式使用offset和length参数来指定复制到目标数组的子区间。
这些批量移动的合成效果与前文所讨论的循环是相同的,但是这些方法可能高效得多,因为这种缓冲区实现能够利用本地代码或其他的优化来移动数据。
批量移动总是具有指定的长度。
也就是说,你总是要求移动固定数量的数据元素。
因此,get(dist)和get(dist,0,dist.length)是等价的。
对于以下几种情况的数据复制会发生异常:
∙如果你所要求的数量的数据不能被传送,那么不会有数据被传递,缓冲区的状态保持不变,同时抛出BufferUnderflowException异常。
∙如果缓冲区中的数据不够完全填满数组,你会得到一个异常。
这意味着如果你想将一个小型缓冲区传入一个大型数组,你需要明确地指定缓冲区中剩余的数据长度。
如果缓冲区存有比数组能容纳的数量更多的数据,你可以重复利用如下代码进行读取:
Java代码
1.byte[]smallArray=newByte[10];
2.
3.while(buffer.hasRemaining()){
4.intlength=Math.min(buffer.remaining(),smallArray.length);
5. buffer.get(smallArray,0,length);
6.//每取出一部分数据后,即调用processData方法,length表示实际上取到了多少字节的数据
7. processData(smallArray,length);
8.}
put()的批量版本工作方式相似,只不过它是将数组里的元素写入buffer中而已,这里不再赘述。
◇创建缓冲区
Buffer的七种子类,没有一种能够直接实例化,它们都是抽象类,但是都包含静态工厂方法来创建相应类的新实例。
这部分讨论中,将以 CharBuffer类为例,对于其它六种主要的缓冲区类也是适用的。
下面是创建一个缓冲区的关键函数,对所有的缓冲区类通用(要按照需要替换类名):
Java代码
1.publicabstractclassCharBufferextendsBufferimplementsCharSequence,Comparable{
2.//ThisisapartialAPIlisting
3.
4.publicstaticCharBufferallocate(intcapacity);
5.publicstaticCharBufferwrap(char[]array);
6.publicstaticCharBufferwrap(char[]array,intoffset,intlength);
7.
8.publicfinalbooleanhasArray();
9.publicfinalchar[]array();
10.publicfinalintarrayOffset();
11.}
新的缓冲区是由分配(allocate)或包装(wrap)操作创建的。
分配(allocate)操作创建一个缓冲区对象并分配一个私有的空间来储存容量大小的数据元素。
包装(wrap)操作创建一个缓冲区对象但是不分配任何空间来储存数据元素。
它使用你所提供的数组作为存储空间来储存缓冲区中的数据元素。
demos:
Java代码
1.//这段代码隐含地从堆空间中分配了一个char型数组作为备份存储器来储存100个char变量。
2.CharBuffercharBuffer=CharBuffer.allocate(100);
3.
4./**
5.*这段代码构造了一个新的缓冲区对象,但数据元素会存在于数组中。
这意味着通过调用put()函数造成的对缓
6.*冲区的改动会直接影响这个数组,而且对这个数组的任何改动也会对这个缓冲区对象可见。
7.*/
8.char[]myArray=newchar[100];
9.CharBuffercharbuffer=CharBuffer.wrap(myArray);
10.
11./**
12.*带有offset和length作为参数的wrap()函数版本则会构造一个按照你提供的offset和length参
13.*数值初始化position和limit的缓冲区。
14.*
15.*这个函数并不像你可能认为的那样,创建了一个只占用了一个数组子集的缓冲区。
这个缓冲区可以存取这个数组
16.*的全部范围;offset和length参数只是设置了初始的状态。
调用clear()函数,然后对其进行填充,
17.*直到超过limit,这将会重写数组中的所有元素。
18.*
19.*slice()函数可以提供一个只占用备份数组一部分的缓冲区。
20.*
21.*下面的代码创建了一个position值为12,limit值为54,容量为myArray.length的缓冲区。
22.*/
23.CharBuffercharbuffer=CharBuffer.wrap(myArray,12,42);
通过allocate()或者wrap()函数创建的缓冲区通常都是间接的。
间接的缓冲区使用备份数组,你可以通过上面列出的api函数获得对这些数组的存取权。
boolean型函数hasArray()告诉你这个缓冲区是否有一个可存取的备份数组。
如果这个函数的返回true,array()函数会返回这个缓冲区对象所使用的数组存储空间的引用。
如果hasArray()函数返回false,不要调用array()函数或者arrayOffset()函数。
如果你这样做了你会得到一个UnsupportedOperationException异常。
如果一个缓冲区是只读的,它的备份数组将会是超出limit的,即使一个数组对象被提供给wrap()函数。
调用array()函数或arrayOffset()会抛出一个ReadOnlyBufferException异常以阻止你得到存取权来修改只读缓冲区的内容。
如果你通过其它的方式获得了对备份数组的存取权限,对这个数组的修改也会直接影响到这个只读缓冲区。
arrayOffset(),返回缓冲区数据在数组中存储的开始位置的偏移量(从数组头0开始计算)。
如果你使用了带有三个参数的版本的wrap()函数来创建一个缓冲区,对于这个缓冲区,arrayOffset()会一直返回0。
不理解吗?
offset和length只是指示了当前的position和limit,是一个瞬间值,可以通过clear()来从0重新存数据,所以arrayOffset()返回的是0。
当然,如果你切分(slice()函数)了由一个数组提供存储的缓冲区,得到的缓冲区可能会有一个非0的数组偏移量。
◇复制缓冲区
缓冲区不限于管理数组中的外部数据,它们也能管理其他缓冲区中的外部数据。
当一个管理其他缓冲器所包含的数据元素的缓冲器被创建时,这个缓冲器被称为视图缓冲器。
视图存储器总是通过调用已存在的存储器实例中的函数来创建。
使用已存在的存储器实例中的工厂方法意味着视图对象为原始存储器的你部实现细节私有。
数据元素可以直接存取,无论它们是存储在数组中还是以一些其他的方式,而不需经过原始缓冲区对象的get()/put()API。
如果原始缓冲区是直接缓冲区,该缓冲区(视图缓冲区)的视图会具有同样的效率优势。
继续以CharBuffer为例,但同样的操作可被用于任何基本的缓冲区类型。
用于复制缓冲区的api:
Java代码
1.publicabstractclassCharBufferextendsBufferimplementsCharSequence,Comparable{
2.//ThisisapartialAPIlisting
3.
4.publicabstractCharBufferduplicate();
5.publicabstractCharBufferasReadOnlyBuffer();
6.publicabstractCharBufferslice();
7.}
●duplidate()
复制一个缓冲区会创建一个新的Buffer对象,但并不复制数据。
原始缓冲区和副本都会操作同样的数据元素。
duplicate()函数创建了一个与原始缓冲区相似的新缓冲区。
两个缓冲区共享数据元素,拥有同样的容量,但每个缓冲区拥有各自的position、limit和mark属性。
对一个缓冲区你的数据元素所做的改变会反映在另外一个缓冲区上。
这一副本缓冲区具有与原始缓冲区同样的数据视图。
如果原始的缓冲区为只读,或者为直接缓冲区,新的缓冲区将继承这些属性。
duplicate()复制缓冲区:
Java代码
1.CharBufferbuffer=CharBuffer.allocate(8);
2.buffer.position(3).limit(6).mark().position(5);
3.CharBufferdupeBuffer=buffer.duplicate();
4.buffer.clear();
复制一个缓冲区
●asReadOnlyBuffer()
asReadOnlyBuffer()函数来生成一个只读的缓冲区视图。
这与duplicate()相同,除了这个新的缓冲区不允许使用put(),并且其isReadOnly()函数将会返回true。