缓冲区的基类:Buffer
Buffer有四个属性:
1、capacit(容量)
2、limit(上界)
3、position(位置)
4、mark(标记)
*:绝对存储不会影响缓冲区的位置属性
存和取的方法:
public abstract byte get( );
public abstract byte get (int index);
public abstract ByteBuffer put (byte b);
public abstract ByteBuffer put (int index, byte b);
Get和put可以是相对的或者是绝对的。在前面的程序列表中,相对方案是不带有索引参数的函数。当相对函数被调用时,位置在返回时前进一。如果位置前进过多,相对运算就会抛出异常。对于put(),如果运算会导致位置超出上界,就会抛出BufferOverflowException异常。对于get(),如果位置不小于上界,就会抛出BufferUnderflowException异常。绝对存取不会影响缓冲区的位置属性,但是如果您所提供的索引超出范围(负数或不小于上界),也将抛出IndexOutOfBoundsException异常。
翻转:
Buffer.flip()
这是一个封装好了的函数,它的功能与下面函数执行的功能是等效的
buffer.limit(buffer.position()).position(0)
这样做的目的是:当你对一个缓冲区装载完毕时,你需要改变它的位置状态让它准备好被取出数据。所以我们把它的位置定位为0,上界定位为当前加载到的位置。
书本上的解释:
Flip()函数将一个能够继续添加数据元素的填充状态的缓冲区翻转成一个准备读出元素的释放状态。在翻转之后,图2.4的缓冲区会变成下图中的样子。
释放数据
在对数据释放前,一定要确保对缓冲区进行了一次翻转操作(并且只能有一次)
使用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( );
}
如果您对缓冲区有专门的控制,这种方法会更高效,因为上界不会在每次循环重复时都被检查(这要求调用一个buffer样例程序)。上文中的第一个例子允许多线程同时从缓冲区释放元素。
*:缓冲区的存取不是线程安全的。使用多线程一定要进行同步
使用compact()函数可以压缩未读的缓冲区
以下是JDK API中的解释:
public abstract CharBuffer compact()压缩此缓冲区(可选操作)。
将缓冲区当前位置和界限之间的字符(如果有)复制到缓冲区的开始处。即将索引 p = position() 处的字符复制到索引 0 处,将索引 p + 1 处的字符复制到索引 1 处,依此类推,直到将索引 limit() - 1 处的字符复制到索引 n = limit() - 1 - p 处。然后将缓冲区的位置设置为 n+1,并将其界限设置为其容量。如果已定义了标记,则丢弃它。
将缓冲区的位置设置为复制的字符数,而不是零,以便调用此方法后可以紧接着调用另一个相对 put 方法。
书上解释:调用compact()的作用是丢弃已经释放的数据,保留未释放的数据,并使缓冲区对重新填充容量准备就绪。
如果您想在压缩后释放数据,缓冲区会像之前所讨论的那样需要被翻转。无论您之后是否要向缓冲区中添加新的数据,这一点都是必要的。
标记
这本章节的开头,我们已经涉及了缓冲区四种属性中的三种。第四种,标记,使缓冲区能够记住一个位置并在之后将其返回。缓冲区的标记在mark( )函数被调用之前是未定义的,调用时标记被设为当前位置的值。reset( )函数将位置设为当前的标记值。如果标记值未定义,调用reset( )将导致InvalidMarkException异常。一些缓冲区函数会抛弃已经设定的标记(rewind( ),clear( ),以及flip( )总是抛弃标记)。如果新设定的值比当前的标记小,调用limit( )或position( )带有索引参数的版本会抛弃标记。
缓冲区的比较
缓冲区之前可以使用equals()函数或compareTo()函数进行比较。
如果两个缓冲区中剩余的内容相同,那么equals()函数会返回true。
两个缓冲区相等的充要条件:
两个对象类型相同。包含不同数据类型的buffer永远不会相等,而且buffer绝不会等于非buffer对象。
两个对象都剩余同样数量的元素。Buffer的容量不需要相同,而且缓冲区中剩余数据的索引也不必相同。但每个缓冲区中剩余元素的数目(从位置到上界)必须相同。
在每个缓冲区中应被Get()函数返回的剩余数据元素序列必须一致。
批量移动
public CharBuffer get (char [] dst)
public CharBuffer get (char [] dst, int offset, int length) 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)
批量传输的大小总是固定的。省略长度意味着整个数组会被填满。
如果缓冲区的长度没有指定数组的长度长,那么整个传输都会失败,并且会抛出一个BufferUnderflowException异常
缓冲区对象的创建
以CharBuffer为例。
1、使用静态函数allocate创建指定大小的缓冲区:
CharBuffer charBuffer = CharBuffer.allocate (100);
2、使用静态函数warp将数组转化为Charbuffer,注意数组的类型必须和Buffer匹配
char [] myArray = new char [100];
CharBuffer charbuffer = CharBuffer.wrap (myArray);
warp还可加入两个参数分别作为position和limit
使用上两种方式创建的缓冲区通常都是间接的,间接的缓冲区使用备份数组,可以使用hasArray函数判断缓冲区是否有一个可存取的备份数组,使用array函数可以得到备份数组,任何对备份数组的更改都会映射到缓冲区
缓冲区的复制
以CharBuffer为例
API中提供了以下方法:
public abstract CharBuffer duplicate( );
public abstract CharBuffer asReadOnlyBuffer( );
public abstract CharBuffer slice( );
duplicate函数创建了一个与原始缓冲区相似的缓冲区,每个缓冲区有自己的位置信息,但对缓冲区的修改都会映射到同一个底层数组上。
slice创建一个分割缓冲区:分割缓冲区与复制相似,但slice()创建一个从原始缓冲区的当前位置开始的新缓冲区,并且其容量是原始缓冲区的剩余元素数量(limit-position)。这个新缓冲区与原始缓冲区共享一段数据元素子序列。分割出来的缓冲区也会继承只读和直接属性。
字节缓冲区
关于字节存储顺序:
只有ByteBuffer的字符顺序设定可以随时通过调用以ByteOrder.BIG_ENDIAN或ByteOrder.LITTL_ENDIAN为参数的order()函数来改变。
如果一个缓冲区被创建为一个ByteBuffer对象的视图(参见2.4.3节),那么order()返回的数值就是视图被创建时其创建源头的ByteBuffer的字节顺序设定。视图的字节顺序设定在创建后不能被改变,而且如果原始的字节缓冲区的字节顺序在之后被改变,它也不会受到影响。。
直接缓冲区:
调用ByteBuffer.allocateDirect()函数产生
它直接与JVM低底层的IO交互,效率最高
字节缓冲区可以创建视图:
public abstract CharBuffer asCharBuffer(); public abstract ShortBuffer asShortBuffer(); public abstract IntBuffer asIntBuffer(); public abstract LongBuffer asLongBuffer(); public abstract FloatBuffer asFloatBuffer(); public abstract DoubleBuffer asDoubleBuffer();
ByteBuffer类提供了一个不太重要的机制来以多字节数据类型的形式存取byte数据组。ByteBuffer类为每一种原始数据类型提供了存取的和转化的方法:
public abstract class ByteBuffer extends Buffer implements Comparable {
public abstract char getChar( );
public abstract char getChar (int index);
public abstract short getShort( );
public abstract int getInt( );
public abstract long getLong( );
public abstract float getFloat (int index);
public abstract double getDouble( );
public abstract double getDouble (int index);
public abstract ByteBuffer putChar (char value);
public abstract ByteBuffer putChar (int index, char value);
public abstract ByteBuffer putShort (short value);
public abstract ByteBuffer putShort (int index, short value);
public abstract ByteBuffer putInt (int value);
public abstract ByteBuffer putInt (int index, int value);
public abstract ByteBuffer putLong (long value);
public abstract ByteBuffer putLong (int index, long value);
public abstract ByteBuffer putFloat (float value);
public abstract ByteBuffer putFloat (int index, float value);
public abstract ByteBuffer putDouble (double value);
public abstract ByteBuffer putDouble (int index, double value);
}