Java NIO 缓冲区 Buffer
缓冲区 Buffer 是 Java NIO 中一个核心概念,它是一个线性结构,容量有限,存放原始类型数据(boolean 除外)的容器。
1. Buffer 中可以存放的数据类型
java.nio.Buffer 是一个接口,有 7 个重要的子类,对应着 7 种(除 boolean 外)原始数据类型:IntBuffer, CharBuffer, FloatBuffer, DoubleBuffer, ShortBuffer, LongBuffer, ByteBuffer。
.st22 {fill:#191919;font-family:Times New Roman;font-size:8pt}
.st11 {fill:#191919;font-family:Times New Roman;font-size:9pt}
BufferIntBufferFloatBufferCharBufferLongBufferByteBufferDoubleBufferShortBufferHeapIntBufferHeapFloatBufferHeapCharBufferHeapLongBufferHeapByteBufferHeapDoubleBufferHeapShortBufferMappedByteBufferDirectByteBuffer
从类图中可以看到,7 种数据类型对应着 7 种子类,这些名字是 Heap 开头子类,数据是存放在 JVM 堆中的;而 MappedByteBuffer 则是存放在堆外的直接内存中,可以映射到文件。
2. 创建一个 Buffer
上面所列举的 7 个类是抽象类,不能直接使用 new 关键字来实例化,而应该调用 allocate(int capacity) 方法来实例化一个 Buffer。
CharBuffer buf = CharBuffer.allocate(10);
每个 Buffer 都有其固定的容量,上面这句代码表示创建一个容量为 10 的 CharBuffer,最多可以往 Buffer 中存放 10 个 char 字符。Buffer 中有 3 个重要的属性 capacity, limit, position:
capacity 是一个不可改变的非负整数,它在创建 Buffer 的时候设置。如上面创建的 buf 的 capacity 就是 10。
limit 也是一个非负整数,其值不能超过 capacity。它指向 Buffer 中第 1 个不能够被读或者写的元素。
position 表示 Buffer 中下一个被读取或者写入的单元。
下图为一个 CharBuffer 刚创建时的结构。它的内部存储结构是一个 char[] 数组,capacity 和 limit 指向 10 的位置,position 指向位置 0 。同理,一个 IntBuffer 内部的存储结构为一个 int[] 数组。
.st1 {fill:#191919;font-family:Consolas;font-size:10pt}
.st3 {fill:#191919;font-family:Times New Roman;font-size:12pt}
.st2 {font-family:Arial;font-size:10pt}
7856923014capacity / limitpositionallocate(10)可写
上面分配的缓冲区存储结构是数组,数组是存放在虚拟机堆内存中的,这一类 Buffer 也可以称为堆缓冲区;事实上,调用 CharBuffer.allocate(capacity) 方法分配的缓冲区就是一个 HeapCharBuffer 的实例。相应地,有 7 种堆缓冲区。不过 ByteBuffer 有些特殊,它除了有常规地 HeapByteBuffer 之外,还有一个 MappedByteBuffer,它基于内存映射技术,内容是文件在内存中的映射;使用 MappedByteBuffer 访问大文件效率要比 HeapByteBuffer 更高,这里不作讨论,其它文章有单独的介绍。
3. Buffer 的读和写
一个 Buffer 可以处于读模式或者写模式,两种模式可以发生切换。一个刚创建的 Buffer 处于写模式,此时可以往里面写入数据。
3.1 put(char c) 写入数据
put(char c) 方法可以往 Buffer 中写入数据。CharBuffer 使用了建造者设计模式,大部分方法都返回了当前对象,可以使用链式调用往 Buffer 中写入数据。
buf.put('A').put('B').put('C').put('D').put('E').put('F').put('G');
每写入一个 char 数据,position 就移动 1 位。
.st1 {fill:#191919;font-family:Consolas;font-size:10pt}
.st2 {fill:#191919;font-family:Times New Roman;font-size:12pt}
.st3 {font-family:Arial;font-size:10pt}
7856923014capacity / limitposition可写put('A') ... put('G')'F''G''C''D''A''B''E'7856923014capacity / limitposition可写
3.2 flip() 将写模式切换到读模式
flip() 能够将 Buffer 从写入模式切换成读取模式,它的原理很简单,就是将 limit 移动到了 position 所指位置,将 position 移到了 0 位置。
buf.flip();
buf 内部结构的变化如下图所示。
.st1 {fill:#191919;font-family:Consolas;font-size:10pt}
.st2 {fill:#191919;font-family:Times New Roman;font-size:12pt}
.st3 {font-family:Arial;font-size:10pt}
'F''G''C''D''A''B''E'7856923014capacity / limitposition可写'F''G''C''D''A''B''E'7856923014capacityposition可读limitflip()
3.3 get() 读取数据
get() 方法能够读取 Buffer 中的数据,它将 position 所指的元素返回,然后 position 移动 1 个位置,指向下一个应该返回的元素。get() 因为要返回元素,所以它不支持链式调用。
System.out.println(buf.get()); // 输出:A
System.out.println(buf.get()); // 输出:B
System.out.println(buf.get()); // 输出:C
.st1 {fill:#191919;font-family:Consolas;font-size:10pt}
.st2 {fill:#191919;font-family:Times New Roman;font-size:12pt}
.st3 {font-family:Arial;font-size:10pt}
'F''G''C''D''A''B''E'7856923014capacityposition可读limit'F''G''C''D''A''B''E'7856923014capacityposition可读limitget(),get(), get()
3.4 mark() / reset() / rewind()
mark() 将记录当前 positon 的位置,reset() 方法需要配合 mark() 使用。调用 mark() 方法的时候,position 将赋值给 mark 属性;可以继续读取,position 往前移动;调用 reset() 之后,position 会重新回退到 mark 位置。
buf.mark();
System.out.println(buf.get()); // 输出:D
buf.reset();
.st1 {fill:#191919;font-family:Consolas;font-size:10pt}
.st2 {fill:#191919;font-family:Times New Roman;font-size:12pt}
.st3 {font-family:Arial;font-size:10pt}
'F''G''C''D''A''B''E'7856923014capacityposition可读limit'F''G''C''D''A''B''E'7856923014capacitymark可读limitmark() get()position'F''G''C''D''A''B''E'7856923014mark/position可读limitcapacityreset()
mark() 配合 reset() 允许重新读取部分元素,rewind() 允许重新读取全部元素。rewind() 方法直接让 position 指向 0 位置,然后将 mark 清除。
3.5 compact()
想象这样一个场景:某个 Buffer 中存储了一条半消息,剩下半条消息的数据还没有全部传输过来;此时需要让 Buffer 切换到读取模式,读取第 1 条消息,让后让剩下的半条消息的数据留在 Buffer 中。compact() 可以很好地处理这样地场景。
compact() 可以将一个未读取完的 Buffer 切换到写模式。它的具体做法是将 [position, limit) 范围内的数据复制到 [0, limit-position),然后 position = limit - position, limit = capacity。此时又可以往 Buffer 中写数据而不会影响未读取的数据了。
buf.compact();
System.out.println(Arrays.toString(buf.array())); // 输出:[D, E, F, G, E, F, G, , , ]
需要注意的是,compact() 仅仅是复制了数据,而并没有将数组中的其它元素设置为零值,如下图所示。
.st1 {fill:#191919;font-family:Consolas;font-size:10pt}
.st2 {fill:#191919;font-family:Times New Roman;font-size:12pt}
.st3 {font-family:Arial;font-size:10pt}
'F''G''C''D''A''B''E'7856923014position可读limit'F''G''F''G''D''E''E'7856923014position可写capacitycapacity / limitcompact()
3.6 clear()
clear() 方法可以认为是将 Buffer 重置了,将 Buffer 切换到了写模式。实际上它只是将 position, limit 的值重置了,position=0, limit = capacity。
4. 小结
Buffer 是一个缓冲数据的容器,其内部结构是一个固定长度的数组,支持 7 种数据的存取。
Buffer 是非线程安全的,多线程并发访问一个 Buffer 时应该进行同步。
Buffer 有读取和写入两种模式,刚创建时处于写入模式,模式之间可以进行切换。其模式切换关系如下图所示。
.st1 {fill:#191919;font-family:Times New Roman;font-size:12pt}
.st2 {font-family:Arial;font-size:10pt}
可写可读allocate()flip()get() / mark() / reset() / rewind()compact() / clear()put()
需要明确的是,一般情况下,Buffer 中并没有明确限制读取模式中 Buffer 只能读取,写入模式中只能写入。例如一个刚创建的 buf 也可以直接调用 get() 方法。不过按照这种模式的限制操作一个 Buffer 不容易使数据出错。另外,Buffer 中也提供了 asReadOnlyBuffer() 方法来基于原 Buffer 创建一个新的只读 Buffer。
Java NIO 缓冲区 Buffer的更多相关文章
- Java NIO -- 缓冲区(Buffer)的数据存取
缓冲区(Buffer): 一个用于特定基本数据类型的容器.由 java.nio 包定义的,所有缓冲区都是 Buffer 抽象类的子类.Java NIO 中的 Buffer 主要用于与 NIO 通道进行 ...
- JAVA NIO缓冲区(Buffer)------ByteBuffer常用方法
参考:https://blog.csdn.net/xialong_927/article/details/81044759 缓冲区(Buffer)就是在内存中预留指定大小的存储空间用来对输入/输出(I ...
- Java NIO之Buffer(缓冲区)
Java NIO中的缓存区(Buffer)用于和通道(Channel)进行交互.数据是从通道读入缓冲区,从缓冲区写入到通道中的. 缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存.这 ...
- java nio 缓冲区(一)
本文来自于我的个人博客:java nio 缓冲区(一) 我们以Buffer类開始对java.nio包的浏览历程.这些类是java.nio的构造基础. 这个系列中,我们将尾随<java NIO ...
- Java NIO 之 Buffer
Java NIO 之 Buffer Java NIO (Non Blocking IO 或者 New IO)是一种非阻塞IO的实现.NIO通过Channel.Buffer.Selector几个组件的协 ...
- JAVA NIO简介-- Buffer、Channel、Charset 、直接缓冲区、分散和聚集、文件锁
IO 是主存和外部设备 ( 硬盘.终端和网络等 ) 拷贝数据的过程. IO 是操作系统的底层功能实现,底层通过 I/O 指令进行完成. Java标准io回顾 在Java1.4之前的I/O系统中,提供 ...
- Java NIO 之 Buffer(缓冲区)
一 Buffer(缓冲区)介绍 Java NIO Buffers用于和NIO Channel交互. 我们从Channel中读取数据到buffers里,从Buffer把数据写入到Channels. Bu ...
- 【Java nio】buffer
package com.slp.nio; import org.junit.Test; import java.nio.ByteBuffer; /** * Created by sanglp on 2 ...
- java nio之Buffer
一.JAVA NIO 是在和channel交互的时候使用的.Channel将数据读入缓冲区,然后我们又从缓冲区访问数据.写数据时,首先将要发送的数据按顺序填入缓冲区.基本上,缓冲区只是一个列表,它的所 ...
随机推荐
- 【CSP-S 2019】树的重心(重心的性质)
Description 给定一颗 \(n\) 个顶点的树 \(\text T\),共 \(n-1\) 次断边操作,每次将树分为两部分 \(\text T_1, \text T_2\),求: \[\su ...
- (干货)构建镜像之Dockerfile
Dockerfile是一个文本文件,记录了镜像构建的所有步骤. 饭提示:学习Dockerfile构建镜像,就是在学习Dockerfile文件构建的命令+shell脚本语句 Dockerfile简单介绍 ...
- Jmeter(7)参数化csv data set config
接口测试同一变量或同一组变量不同值时,可通过csv data set config配置数据 1.创建文本文件,写入参数值,一个或一组值为一行,保存为.csv文件 2.创建测试计划,配置元件添加csv ...
- Java8新增的这些集合骚操作,你掌握了嘛?
目录 Iterable的forEach Iterator的forEachRemaining Collection的removeIf Stream操作 List的replaceAll List的sort ...
- 记一次HBase的NotServingRegionException问题
1. 问题 在hbase测试集群上,访问一些hbase表,均报类似下面的错误:region不可用 Wed Oct 28 14:00:56 CST 2020, RpcRetryingCaller{glo ...
- vue第八单元(组件通信 子父,父子组件通信 自定义事件 事件修饰符 v-model props验证 )
第八单元(组件通信 子父,父子组件通信 自定义事件 事件修饰符 v-model props验证 ) #课程目标 掌握使用props让父组件给子组件传参(重点) 掌握props属性的使用以及prop验证 ...
- 推荐系统实践 0x11 NeuralCF
前言 这一篇文章我们来谈一下2017年新加坡国立大学提出的基于深度学习的系统过滤模型NeuralCF.我们在之前讲过矩阵分解技术,将协同过滤中的共现矩阵分解成用户向量矩阵以及物品向量矩阵.那么Embe ...
- 在wildfly中使用SAML协议连接keycloak
目录 简介 OpenID Connect和SAML SAML的工作流程 在keycloak中使用SAML 准备wildfy和应用程序 简介 我们知道SSO的两个常用的协议分别是SAML和OpenID ...
- 不一样的资产安全 3D 可视化平台
前言 数字经济时代,应用好数据是企业数字化转型的关键,基于前沿科学技术进行数据的有效管控,更是对数字增值服务的新趋势.近年来,整个安全行业对资产管理的重视程度正在提高.据IDC发布的相关数据显示, ...
- 关于 ReentrantLock 中锁 lock() 和解锁 unlock() 的底层原理浅析
关于 ReentrantLock 中锁 lock() 和解锁 unlock() 的底层原理浅析 如下代码,当我们在使用 ReentrantLock 进行加锁和解锁时,底层到底是如何帮助我们进行控制的啦 ...