深入理解ByteBuffer

转载自  深入理解ByteBuffer

ByteBuffer类是在Java NIO中常常使用的一个缓冲区类,使用它可以进行高效的IO操作,但是,如果对常用方法的理解有错误,那么就会出现意想不到的bug。

ByteBuffer类的常用方法

先来看看一个基本的程序

public void test() throws IOException{ByteBuffer buff = ByteBuffer.allocate(128);FileChannel fin = null;FileChannel fout = null;try{fin = new FileInputStream("filein").getChannel();fout = new FileOutputStream("fileout").getChannel();while(fin.read(buff) != -1) {buff.flip();fout.write(buff);buff.clear();}}catch (FileNotFoundException e){} finally {try {if(fin != null) {fin.close();}if(fout != null) {fout.close();}} catch(IOException e) {throw e;}}}

在test方法中,首先通过ByteBuffer.allocate()方法分配了一段内存空间,作为缓存,allocate方法对缓存自动清零,然后打开一个输入文件管道fin和一个输出文件管道fout,在循环中先从fin读出数据存放到buff缓冲区中,再将buff缓冲中的内容写入fout。当然这对于先从文件中读,然后再写这样的场景,这不是高效的做法。 
可以看到先从fin中读出数据后,首先要调用ByteBuffer.flip()方法,若将数据写入输出文件后,还要调用ByteBuffer.clear()方法,为什么要这样做呢?

ByteBuffer可以作为一个缓冲区,是因为它是内存中的一段连续的空间,在ByteBuffer对象内部定义了四个索引,分别是mark,position,limit,capacity,其中

  • mark用于对当前position的标记

  • position表示当前可读写的指针,如果是向ByteBuffer对象中写入一个字节,那么就会向position所指向的地址写入这个字节,如果是从ByteBuffer读出一个字节,那么就会读出position所指向的地址读出这个字节,读写完成后,position加1

  • limit是可以读写的边界,当position到达limit时,就表示将ByteBuffer中的内容读完,或者将ByteBuffer写满了。

  • capacity是这个ByteBuffer的容量,上面的程序中调用ByteBuffer.allocate(128)就表示创建了一个容量为capacity字节的ByteBuffer对象。

了解了这四个变量之后,再来看看前面的程序。之所以调用ByteBuffer.flip()方法是因为在向ByteBuffer写入数据后,position为缓冲区中刚刚读入的数据的最后一个字节的位置,flip方法将limit值置为position值,position置0,这样在调用get*()方法从ByteBuffer中取数据时就可以取到ByteBuffer中的有效数据,JDK中flip方法的代码如下:

public final Buffer flip() {limit = position;position = 0;mark = -1;return this;
}

在调用four.write(buff)时,就将buff缓冲区中的数据写入到输出管道,此时调用ByteBuffer.clear()方法为下次从管道中读取数据做准备,但是调用clear方法并不将缓冲区的数据清空,而是设置position,mark,limit这三个变量的值,JDK中clear方法的代码如下:

public final Buffer clear() {position = 0;limit = capacity;mark = -1;return this;
}

这个方法命名给人的感觉就是将数据清空了,但是实际上却不是的,它并没有清空缓冲区中的数据,而至重置了对象中的三个索引值,如果不清空的话,假设此次该ByteBuffer中的数据是满的,下次读取的数据不足以填满缓冲区,那么就会存在上一次已经处理的的数据,所以在判断缓冲区中是否还有可用数据时,使用ByteBuffer.hasRemaining()方法,在JDK中,这个方法的代码如下:

public final boolean hasRemaining() {return position < limit;
}

在该方法中,比较了position和limit的值,用以判断是否还有可用数据。

在ByteBuffer类中,还有个方法是compact,对于ByteBuffer,其子类HeapByteBuffer的compact方法实现是这样的:

public ByteBuffer compact() {System.arraycopy(hb, ix(position()), hb, ix(0), remaining());position(remaining());limit(capacity());return this;
}

如果position()方法返回当前缓冲区中的position值,remaining()方法返回limit与position这段区间的长度,JDK中的remaining()方法代码如下

public final int remaining() {return limit - position;
}

所以compact()方法中第一条语句作用是将数组hb当前position所指向的位置开始复制长度为limit-position的数据到hb数组的开始出,其中使用到了ix()函数,这个函数是将参数值加上一个offset值,offset即一个偏移值,在这样的比如一个这样的场景对于一个很大的缓冲区,将其分成两段,第一段的起始位置是p1,长度是q1,第二段起始位置是p2,长度是q2,那么可以分别将这两段包装成一个HeapByteBuffer对象,然后这两个HeapByteBuffer对象(ByteBuffer的子类,默认实现)的offset属性分别设置为p1和p2,这样就可以通过在内部使用ix()函数来简化ByteBuffer对外提供的接口,在使用者看来,与默认的ByteBuffer并没有区别。

在compact函数中,接着将当前的缓冲区的position索引置为limit-position,limit索引置为缓冲区的容量,这样调用compact方法中就可以将缓冲区的有效数据全部移到缓冲区的首部,而position指向下一个可写位置。

比如刚刚创建一个ByteBuffer对象buff时,position=0,limit=capacity,那么此时调用buff.hasRemaining()则会返回true,这样来判断缓冲区中是否有数据是不行的,因为此时缓冲区中的存储的全部是0,但是调用一次compact()方法就可以将position置为limit值,这样再通过buff.hasRemaining()就会返回false,可以与后面的逻辑一起处理了。

ByteBuffer还有一个名为mark的方法,该方法设置mark索引为position的值,JDK中的代码如下:

public final Buffer mark() {mark = position;return this;
}

与其功能相反的方法为reset方法,即将position的值设置为mark,JDK中的代码如下:

public final Buffer reset() {int m = mark;if (m < 0)throw new InvalidMarkException();position = m;return this;
}

此外还有一个名为rewind的方法,这个方法将position索引置为0,mark索引置为-1,JDK中的代码如下:

public final Buffer rewind() {position = 0;mark = -1;return this;
}

通过这些方法,就可以很方便的操作一个缓冲区,关键是要理解这些方法具体的作用,以及对三个索引值的影响(capacity是不变的)。

ByteBuffer继承自Buffer类,上面的方法四个索引值都定义在Buffer类中,操作索引值的方法也都定义在Buffer类中。

总结

通过对ByteBuffer中的四个索引值操作方法的分析,加深了对ByteBuffer的理解。理解ByteBuffer和其他几种Buffer的关键是要理解在使用中各个方法是如何操作索引值的,特别要注意的是clear方法并没有清除缓冲区的内容。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/328996.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

c语言倒置存放,c语言倒置

为什么使用人脸识别返回数据为空&#xff1f; 问题现象 上传照片中的人像可能存在横置或倒置等情况。 解决方法 尽量选择纯色无干扰背景&#xff0c;保证图片中人像清晰。 上传的人脸图片要求&#xff1a;侧脸不超过30&#xff0c;抬头低头不超过15。 图片中人脸需要保持竖置正…

当当网新用户注册界面——界面源码

<strong><span style"font-size:32px;color:#ff9966;">所有用到的图片都已上传&#xff0c;请在<a target_blank href"http://download.csdn.net/detail/qq_34137397/9667142">这里</a>下载</span></strong><!DOCT…

dotnetCore增加MiddleWare的Run,Use Map MapThen四个扩展方法

什么是中间件 中间件是在管道中处理Request请求与Responses响应的一种组件&#xff0c;每种组件可以选择是否让Request进入到下一个组件去处理。 译得不好&#xff0c;大家可以自己看原文Middleware 更详细的还可以参照园中大神的作品&#xff1b; 有汤姆大叔的解读ASP.NET 5 &…

Java GC系列(1):Java垃圾回收简介

转载自 Java GC系列&#xff08;1&#xff09;&#xff1a;Java垃圾回收简介这篇教程是系列第一部分。首先会解释基本的术语&#xff0c;比如JDK、JVM、JRE和HotSpotVM。接着会介绍JVM结构和Java 堆内存结构。理解这些基础对于理解后面的垃圾回收知识很重要。 Java关键术语 Ja…

下载的c语言程序代码怎么运行,CFree怎么运行程序 编译运行C语言程序代码的方法...

CFree是一款C语言编译软件&#xff0c;用户可以利用这款软件编译C/C程序 &#xff0c;如果你想要运行已经编写好的C语言代码&#xff0c;只需要几个简单的操作即可实现&#xff0c;如果你还不知道怎么运行&#xff0c;就赶快来看看下面的教程吧&#xff01;1、首先需要先进入到…

docker4dotnet #2 容器化主机

.NET 猿自从认识了小鲸鱼&#xff0c;感觉功力大增。上篇 《docker4dotnet #1 前世今生 & 世界你好》中给大家介绍了如何在Windows上面配置Docker for Windows和Docker Tools for Visual Studio来使用docker协助.NET Core应用的开发&#xff0c;这篇我们来看看如何创建和管…

Java GC系列(2):Java垃圾回收是如何工作的

转载自 Java GC系列&#xff08;2&#xff09;&#xff1a;Java垃圾回收是如何工作的本教程是为了理解基本的Java垃圾回收以及它是如何工作的。这是垃圾回收教程系列的第二部分。希望你已经读过了第一部分&#xff1a;《Java 垃圾回收介绍》。 Java 垃圾回收是一项自动化的过…

c语言程序为什么运行一半就自动关闭了,C语言为什么程序运行一半就出现停止工作...

匿名用户1级2016-04-04 回答有点儿意思。原因却出乎意料的简单。 char *rwtab[27]{"main","if","then","while","do"," static","int"," double","struct","break",&quo…

docker4dotnet #1 – 前世今生 amp; 世界你好

作为一名.NET Developer&#xff0c;这几年看着docker的流行实在是有些眼馋。可惜的是&#xff0c;Docker是基于Linux环境的&#xff0c;眼瞧着那些 java, python, node.js, go 甚至连php程序员都可以docker了&#xff0c;自己还在苦哈哈的装虚拟机&#xff0c;实在是急啊&…

Java GC系列(3):垃圾回收器种类

转载自 Java GC系列&#xff08;3&#xff09;&#xff1a;垃圾回收器种类在这篇教程中我们将学习几种现有的垃圾回收器。在Java中&#xff0c;垃圾回收是一个自动的进程可以替代程序员进行内存的分配与回收这些复杂的工作。这篇是垃圾回 收教程系列的第三篇&#xff0c;在前面…

当当网新用户注册界面——CSS代码

<strong><span style"font-size:32px;color:#ff9966;">所有用到的图片都已上传&#xff0c;请在<a target_blank href"http://download.csdn.net/detail/qq_34137397/9667142">这里</a>下载</span></strong> <pre …

计算机二级c语言考试模拟试题,计算机二级C语言考前模拟试题及答案2016

计算机二级C语言考前模拟试题及答案2016(1)有以下程序&#xff1a;程序运行后的输出结果是(   )。A.852B.963C.741D.875421(2)有以下程序&#xff1a;程序运行后的输出结果是(   )。A.let4B.m2C.m6D.m5(3)有以下程序&#xff1a;程序运行后的输出结果是(   )。A.6B.3C.8D…

Visual Studio Code 1.3终于增加了标签页

2016年6月发布的Visual Studio Code 1.3版更新已发布&#xff0c;该版本提供了用户呼声最高的一个功能请求。 很长一段时间来&#xff0c;开发者一直呼吁为Microsoft的跨平台代码编辑器提供标签页功能。在UserVoice网站上有关这个功能的请求已获得超过9,000个投票。 标签页默认…

lego ev3 c语言编程,乐高 EV3 高级编程 – 第一课:安装

我的学生写的教程&#xff0c;英文版的&#xff0c;好吧&#xff0c;我翻译一下&#xff01;先介绍一下我的学生&#xff0c;今年 12 岁 (2018年)&#xff0c;普通 2 等中学初中 2 年级(学校名字就不说了&#xff0c;说了你也没听过&#xff01;)&#xff0c;除了数学和科学比较…

Java GC系列(4):垃圾回收监视和分析

转载自 Java GC系列&#xff08;4&#xff09;&#xff1a;垃圾回收监视和分析在这个Java GC系列教程中&#xff0c;让我们学习用于垃圾回收监视和分析的工具。然后&#xff0c;选用一种工具来监视一个Java示例程序的垃圾回收过程。如果你是一名初学者&#xff0c;你最好仔细阅…

ASP.NET Core开发-使用Nancy框架

Nancy简介 Nancy是一个轻量级的独立的框架&#xff0c;下面是官网的一些介绍: Nancy 是一个轻量级用于构建基于 HTTP 的 Web 服务&#xff0c;基于 .NET 和 Mono 平台&#xff0c;框架的目标是保持尽可能多的方式&#xff0c;并提供一个super-duper-happy-path所有交互。Nancy …

属性 方法c语言,C语言如何实现C++中对象属性和方法

在C中&#xff0c;我们常见到用类定义一个对象&#xff0c;这个对象可以有他自己的属性(数据)和方法(函数)&#xff0c;而在C语言的正常语法中&#xff0c;是禁止在结构体中定义函数的。在一个关于触摸屏的驱动程序中发现&#xff0c;用C语言的也可以实现对象的方法(函数)。首先…

2023年山东省职业院校技能大赛高职组 “软件测试”赛项竞赛任务书

2023年山东省职业院校技能大赛高职组 “软件测试”赛项竞赛任务书 竞赛概述 1.1.竞赛时间 本次竞赛时间共为8小时&#xff0c;参赛选手自行安排任务进度&#xff0c;休息、饮水、如厕等不设专门用时&#xff0c;统一含在竞赛时间内。 1.2.竞赛内容 功能测试的测试计划设计、测试…

当当网新用户注册界面——JS代码

<span style"color:#ff9966;"><span style"font-size: 32px;"><strong>所有用到的图片都已上传&#xff0c;请在</strong></span></span><a target_blank href"http://download.csdn.net/detail/qq_34137397/…

Java 内存区域和GC机制

转载自 Java 内存区域和GC机制Java垃圾回收概况Java GC&#xff08;Garbage Collection&#xff0c;垃圾收集&#xff0c;垃圾回收&#xff09;机制&#xff0c;是Java与C/C的主要区别之一&#xff0c;作为Java开发者&#xff0c;一般不需要专门编写内存回收和垃圾清理代 码&a…