ByteBuffer的心得
2016-08-06 11:37
295 查看
在NIO中,数据的读写操作始终是与缓冲区相关联的(读取时信道(SocketChannel)将数据读入缓冲区,写入时首先要将发送的数据按顺序填入缓冲区)
缓冲区是定长的,基本上它只是一个列表,它的所有元素都是基本数据类型(String并未包含其中,但是String. getBytes就可以了).
Nio缓冲区的7种形式
ByteBuffer
ShortBuffer
IntBuffer
CharBuffer
FloatBuffer
DoubleBuffer
LongBuffer
ByteBuffer是最常用的缓冲区,它提供了读写其他数据类型的方法,且信道的读写方法只接收ByteBuffer
在ByteBuffer的属性中:有以下3种常见属性
m:mark:初始值为-1,标记索引地点
p:position:初始值为0,索引下标
l:limit:最好定义成bytebuffer的长度,即允许可读空间长度
c:capacity:缓冲区能容纳的数据元素的最大数量,创建之后无法被改变
总的关系m
1. 创建ByteBuffer
1.1 使用allocate()静态方法
//创建一个容量为256字节的ByteBuffer,
ByteBuffer buffer=ByteBuffer.allocate(256);
参数为ByteBuffer的长度,这个一定要在前期设计时考虑充足,不然就得重新创建
创建其他缓冲区也是这样的静态方法创建的Buffer父类引用对象 。
1.2 通过包装一个已有的数组来创建如下,通过包装的方法创建的缓冲区保留了被包装数组内保存的数据.
ByteBuffer buffer=ByteBuffer.wrap(byteArray);
如果要将一个字符串存入ByteBuffer,可以如下操作:
String sendString="你好,服务器.
";
ByteBuffer sendBuffer=ByteBuffer.wrap(sendString.getBytes("UTF-8"));
//从ByteBuffer中读出String
Charset cs = Charset.forName ("UTF-8");
byte[] bs = new byte[sendBuffer. limit()];
sendBuffer.get(bs);
String news = new String(bs, cs);
//String news = new String(sendBuffer.array(), cs);
注释里的方法也是可行,只是要注意array()方法不是将ByteBuffer中position到limit段的byte[]数组输出,而是输出缓存区所有的byte[]数组!包括limit之后的!
2.回绕缓冲区
buffer.flip();
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
// session开始写入数据
WriteFuture writeFuture = session.write(data);
这个方法用来将缓冲区准备为数据传出状态,执行以上方法后,输出通道会从数据的开头而不是末尾开始.回绕保持缓冲区中的数据不变,只是准备写入而不是读取.
3.清除缓冲区
buffer.clear();
这个方法实际上也不会改变缓冲区的数据,而只是简单的重置了缓冲区的主要索引值.不必为了每次读写都创建新的缓冲区,那样做会降低性能.相反,要重用现在的缓冲区,在再次读取之前要清除缓冲区.说白了就是mark=-1,position=0的过程
以下为常用方式
4.从套接字通道(信道)读取数据
SocketChannel采用的是非阻塞异步读取流数据,在读取的时候,通常是
ByteBuffer.clear();
SocketChannel.read(ByteBuffer);
执行以上方法后,通道会从socket读取的数据填充此缓冲区,它返回成功读取并存储在缓冲区的字节数.在默认情况下,这至少会读取一个字节,或者返回-1指示数据结束.
如果流中有数据,就会把数据从position开始读到ByteBuffer中,在读取之前ByteBuffer的clear操作会把position置为0,limit置为capability,也就是相当于清空了之前的内容,但是ByteBuffer中数组的内容在read之前是没有改变的.
read之后,通常就是开始从ByteBuffer中提取读到的数据,如果你的数据是以自己定义的数据包的格式进行发送的,那你还需要判断是否读到了数据包的结尾,因为对流数据本身来说是没有结尾这一说的。在提取数据之前,要先把position放到开始读取时的位置,把limit放到当前位置,所以要flip一下,表示从position到limit的位置都是需要的数据。
ByteBuffer.flip();
while(ByteBuffer.hasRemaining()){
byte c=ByteBuffer.get();
if (b == PACKAGE_END) {
//you can return the package here
}else{
//you can append the byte here.like StringBuilder.append().
}
}
这样以来也存在一个问题,当一次读到的ByteBuffer不包含完整的数据包或者包含多个数据包.那么就需要在下一次继续把这些包分拆出来.那么在读取数据的代码处就可以改为,这样就把之前读取到的未完整的包保留了下来:
if(!ByteBuffer.hasRemaining){
ByteBuffer.clear();
SocketChannel.read(ByteBuffer);
}
另外一个可能会用到的操作就是ByteBuffer.rewind(),他会把position置为0,limit保持不变,可以用于重复读取一段数据.
5.向套接字通道(信道)写入数据
socketChannel.write(buffer);
此方法以一个ByteBuffer为参数,试图将该缓冲区中字节写入信道.
6.将ByteBuffer与字符的互转
private byte[] getBytes (char[] chars) {//将字符转为字节(编码)
Charset cs = Charset.forName ("UTF-8");
CharBuffer cb = CharBuffer.allocate (chars.length);
cb.put (chars);
cb.flip ();
ByteBuffer bb = cs.encode (cb)
return bb.array();
}
private char[] getChars (byte[] bytes) {//将字节转为字符(解码)
Charset cs = Charset.forName ("UTF-8");
ByteBuffer bb = ByteBuffer.allocate (bytes.length);
bb.put (bytes);
bb.flip ();
CharBuffer cb = cs.decode (bb);
return cb.array();
}
7. put()方法远比想象的强大
buff.put();可以根据数据类型做相应调整
比如buff.putChar(chars),buff.putDouble(double)
缓冲区是定长的,基本上它只是一个列表,它的所有元素都是基本数据类型(String并未包含其中,但是String. getBytes就可以了).
Nio缓冲区的7种形式
ByteBuffer
ShortBuffer
IntBuffer
CharBuffer
FloatBuffer
DoubleBuffer
LongBuffer
ByteBuffer是最常用的缓冲区,它提供了读写其他数据类型的方法,且信道的读写方法只接收ByteBuffer
在ByteBuffer的属性中:有以下3种常见属性
m:mark:初始值为-1,标记索引地点
p:position:初始值为0,索引下标
l:limit:最好定义成bytebuffer的长度,即允许可读空间长度
c:capacity:缓冲区能容纳的数据元素的最大数量,创建之后无法被改变
总的关系m
1. 创建ByteBuffer
1.1 使用allocate()静态方法
//创建一个容量为256字节的ByteBuffer,
ByteBuffer buffer=ByteBuffer.allocate(256);
参数为ByteBuffer的长度,这个一定要在前期设计时考虑充足,不然就得重新创建
创建其他缓冲区也是这样的静态方法创建的Buffer父类引用对象 。
1.2 通过包装一个已有的数组来创建如下,通过包装的方法创建的缓冲区保留了被包装数组内保存的数据.
ByteBuffer buffer=ByteBuffer.wrap(byteArray);
// wrap(int[] array, int offset, int length)
无论要使用的子数组的偏移量offset ,还是子数组的长度length ,使用这个方法的过程都是缓冲整个数组的过程。
而且buffer与数组创建强耦合联系,数据改变则缓冲区值也会改变,反之亦然
如果要将一个字符串存入ByteBuffer,可以如下操作:
String sendString="你好,服务器.
";
ByteBuffer sendBuffer=ByteBuffer.wrap(sendString.getBytes("UTF-8"));
//从ByteBuffer中读出String
Charset cs = Charset.forName ("UTF-8");
byte[] bs = new byte[sendBuffer. limit()];
sendBuffer.get(bs);
String news = new String(bs, cs);
//String news = new String(sendBuffer.array(), cs);
注释里的方法也是可行,只是要注意array()方法不是将ByteBuffer中position到limit段的byte[]数组输出,而是输出缓存区所有的byte[]数组!包括limit之后的!
2.回绕缓冲区
buffer.flip();
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
// session开始写入数据
WriteFuture writeFuture = session.write(data);
这个方法用来将缓冲区准备为数据传出状态,执行以上方法后,输出通道会从数据的开头而不是末尾开始.回绕保持缓冲区中的数据不变,只是准备写入而不是读取.
3.清除缓冲区
buffer.clear();
这个方法实际上也不会改变缓冲区的数据,而只是简单的重置了缓冲区的主要索引值.不必为了每次读写都创建新的缓冲区,那样做会降低性能.相反,要重用现在的缓冲区,在再次读取之前要清除缓冲区.说白了就是mark=-1,position=0的过程
以下为常用方式
4.从套接字通道(信道)读取数据
SocketChannel采用的是非阻塞异步读取流数据,在读取的时候,通常是
ByteBuffer.clear();
SocketChannel.read(ByteBuffer);
执行以上方法后,通道会从socket读取的数据填充此缓冲区,它返回成功读取并存储在缓冲区的字节数.在默认情况下,这至少会读取一个字节,或者返回-1指示数据结束.
如果流中有数据,就会把数据从position开始读到ByteBuffer中,在读取之前ByteBuffer的clear操作会把position置为0,limit置为capability,也就是相当于清空了之前的内容,但是ByteBuffer中数组的内容在read之前是没有改变的.
read之后,通常就是开始从ByteBuffer中提取读到的数据,如果你的数据是以自己定义的数据包的格式进行发送的,那你还需要判断是否读到了数据包的结尾,因为对流数据本身来说是没有结尾这一说的。在提取数据之前,要先把position放到开始读取时的位置,把limit放到当前位置,所以要flip一下,表示从position到limit的位置都是需要的数据。
ByteBuffer.flip();
while(ByteBuffer.hasRemaining()){
byte c=ByteBuffer.get();
if (b == PACKAGE_END) {
//you can return the package here
}else{
//you can append the byte here.like StringBuilder.append().
}
}
这样以来也存在一个问题,当一次读到的ByteBuffer不包含完整的数据包或者包含多个数据包.那么就需要在下一次继续把这些包分拆出来.那么在读取数据的代码处就可以改为,这样就把之前读取到的未完整的包保留了下来:
if(!ByteBuffer.hasRemaining){
ByteBuffer.clear();
SocketChannel.read(ByteBuffer);
}
另外一个可能会用到的操作就是ByteBuffer.rewind(),他会把position置为0,limit保持不变,可以用于重复读取一段数据.
5.向套接字通道(信道)写入数据
socketChannel.write(buffer);
此方法以一个ByteBuffer为参数,试图将该缓冲区中字节写入信道.
6.将ByteBuffer与字符的互转
private byte[] getBytes (char[] chars) {//将字符转为字节(编码)
Charset cs = Charset.forName ("UTF-8");
CharBuffer cb = CharBuffer.allocate (chars.length);
cb.put (chars);
cb.flip ();
ByteBuffer bb = cs.encode (cb)
return bb.array();
}
private char[] getChars (byte[] bytes) {//将字节转为字符(解码)
Charset cs = Charset.forName ("UTF-8");
ByteBuffer bb = ByteBuffer.allocate (bytes.length);
bb.put (bytes);
bb.flip ();
CharBuffer cb = cs.decode (bb);
return cb.array();
}
7. put()方法远比想象的强大
buff.put();可以根据数据类型做相应调整
比如buff.putChar(chars),buff.putDouble(double)
相关文章推荐
- caffe——cifar10模型训练自己的数据
- Hdu 3694 Fermat Point in Quadrangle(三分)
- Javascript算法练习(七)
- Jsp页面基础语法
- textarea 控件根据文字自动增高
- Ionic 开发环境安装步骤
- js之严格模式
- jquery查找父元素、子元素(个人经验总结)
- CSS3 Fliter的十种特效(一)
- Fedora 20 安装搜狗拼音输入法
- CSS3制作3D旋转视频展示区
- rainyday.js
- jQuery:节点(插入,复制,替换,删除)操作
- angular中刷新显示与ng-bind
- js 字符串转换成数字的三种方法
- 不用任何图片,只用简单的css写出唯美的钟表,就问你行吗?
- javascript中数组的22种方法
- javascript中null和undefined的区别
- js 判断checkbox选中
- jquery ajax调用数据后判断返回值是否为 json 格式