前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java ByteBuffer:如何使用 flip() 和 compact()

Java ByteBuffer:如何使用 flip() 和 compact()

作者头像
gemron的空间
修改2022-08-10 10:55:12
4.6K1
修改2022-08-10 10:55:12
举报
文章被收录于专栏:gemrongemron

在本文中,我将使用一个示例向您展示 JavaByteBuffer是如何工作的,以及 方法flip()compact()它的作用。

文章回答了以下问题:

  • 什么是 一个 ByteBuffer,你需要它做什么?
  • 你如何创建一个ByteBuffer
  • positionlimit以及capacity 值是什么意思?
  • 我如何写入ByteBuffer,如何从中读取?
  • 方法flip()compact()究竟是做什么的?

内容

1 什么是 ByteBuffer,你需要它做什么?

2 如何创建一个ByteBuffer

3 ByteBuffer 位置、限制和容量

4 ByteBuffer 读写周期

4.1 使用 put() 写入 ByteBuffer

4.2 使用 Buffer.flip() 切换到读取模式

4.3 使用 get() 从 ByteBuffer 中读取

4.4 切换到写入模式 - 如何不这样做

4.5 使用 Buffer.compact() 切换到写入模式

4.6 下一个循环

5 总结

什么是 ByteBuffer,你需要它做什么?

您需要ByteBuffer使用所谓的Channel. 这篇文章主要是关于它ByteBuffer本身。要了解如何阅读和写文件ByteBufferFileChannel阅读这篇文章。

AByteBuffer是字节数组的包装器,并提供方便地写入和读取字节数组的方法。该ByteBuffer内部存储的读/写位置和所谓的“极限”。

您可以在以下示例中逐步了解这到底意味着什么。

您可以在我的GitHub Repository 中找到为本文编写的代码。

如何创建一个字节缓冲区

首先,您必须创建ByteBuffer具有给定大小(“容量”)的一个。为此,有两种方法:

  • ByteBuffer.allocate(int capacity)
  • ByteBuffer.allocateDirect(int capacity)

该参数capacity以字节为单位指定缓冲区的大小。

allocate()方法在 Java 堆内存中创建缓冲区,垃圾收集器将在使用后将其删除。

allocateDirect(),另一方面,在本机内存中创建缓冲区,即在堆外。本机内存的优点是可以更快地执行读取和写入操作。原因是相应的操作系统操作可以直接访问这块内存区域,而不必先在Java堆和操作系统之间进行数据交换。这种方法的缺点是较高的分配和解除分配成本。

我们创建一个ByteBuffer大小为 1,000 字节的文件,如下所示:

代码语言:javascript
复制
 var buffer = ByteBuffer.allocate(1000); 

然后我们看看缓冲区的指标 – positionlimitcapacity

代码语言:javascript
复制
 System.out.printf("position = %4d, limit = %4d, capacity = %4d%n", buffer.position(), buffer.limit(), buffer.capacity()); 

(由于我们将在整个示例中重复打印这些指标,因此我们立即将System.out.println()命令提取到一个printMetrics(buffer)方法中。)

我们看到以下输出:

position = 0, limit = 1000, capacity = 1000

这是一个图形表示,以便您可以更好地想象缓冲区。浅黄色区域是空的,可以随后填充。

ByteBuffer mit 位置 = 0,限制 = 1000,容量 = 1000
ByteBuffer mit 位置 = 0,限制 = 1000,容量 = 1000

ByteBuffer 位置、限制和容量

显示指标的含义:

  • position是读/写位置。对于新缓冲区,它始终为 0。
  • limit有两个含义: 当我们写入缓冲区时,limit指示我们可以写入的位置。当我们从缓冲区读取时,limit指示缓冲区包含数据的位置。最初, aByteBuffer始终处于写入模式,并且limit等于capacity- 我们可以将空缓冲区填充到最后。
  • capacity指示缓冲区的大小。它的值 1,000 对应于我们传递给该allocate()方法的 1,000 个字节。它在缓冲区的生命周期内不会改变。

ByteBuffer 读写周期

使用 put() 写入 ByteBuffer

为了写入ByteBuffer,有多种put()方法可以将单个字节、字节数组或其他原始类型(如 char、double、float、int、long、short)写入缓冲区。首先,我们将值 1 的 100 倍写入缓冲区,然后我们再次查看缓冲区指标:

代码语言:javascript
复制
for (int i = 0; i < 100; i++) {
  buffer.put((byte) 1);
}

printMetrics(buffer);

运行程序后,我们看到以下输出:

position = 100, limit = 1000, capacity = 1000

该位置已向右移动 100 个字节;缓冲区现在如下所示:

ByteBuffer,位置 = 100,限制 = 1000,容量 = 1000
ByteBuffer,位置 = 100,限制 = 1000,容量 = 1000

接下来,我们在缓冲区中写入 200 次 2。这次我们使用不同的方法:我们首先填充一个字节数组并将其复制到缓冲区中。最后,我们再次打印指标:

代码语言:javascript
复制
byte[] twos = new byte[200];
Arrays.fill(twos, (byte) 2);
buffer.put(twos);

printMetrics(buffer);

现在我们看到:

position = 300, limit = 1000, capacity = 1000

该位置又向右移动了 200 个字节;缓冲区看起来像这样:

ByteBuffer,位置 = 300,限制 = 1000,容量 = 1000
ByteBuffer,位置 = 300,限制 = 1000,容量 = 1000

使用 Buffer.flip() 切换到读取模式

对于从缓冲区读取,有相应的get()方法。例如,当使用Channel.write(buffer).

由于position不仅指示写入位置,还指示读取位置,因此我们必须position重新设置为 0。

同时,我们设置limit为 300 表示最多可以从缓冲区中读取 300 个字节。

在程序代码中,我们这样做:

代码语言:javascript
复制
buffer.limit(buffer.position());
buffer.position(0);

由于每次从写入模式切换到读取模式时都需要这两行,因此有一种方法可以做到:

代码语言:javascript
复制
buffer.flip();

printMetrics()现在调用显示以下值:

position = 0, limit = 300, capacity = 1000

于是位置指针又回到了缓冲区的开头,limit指向了填充区域的结尾:

ByteBuffer,位置 = 0,限制 = 300,容量 = 1000
ByteBuffer,位置 = 0,限制 = 300,容量 = 1000

使用 get() 从 ByteBuffer 读取

假设我们要写入的通道当前只能占用 300 个字节中的 200 个。我们可以通过为该ByteBuffer.get()方法提供一个 200 字节大小的字节数组来模拟这一点,缓冲区应在其中写入其数据:

代码语言:javascript
复制
buffer.get(new byte[200]);

printMetrics() 现在显示以下内容:

position = 200, limit = 300, capacity = 1000

读取位置已经向右移动了 200 个字节——即到了已经读取数据的末尾,也就是我们还需要读取的数据的开始位置:

ByteBuffer,位置 = 200,限制 = 300,容量 = 1000
ByteBuffer,位置 = 200,限制 = 300,容量 = 1000

切换到写入模式 - 如何这样做

现在要写回缓冲区,您可能会犯以下错误:您设置position了数据的末尾,即 300,然后limit又设置为 1000,这使我们回到了写完 1 和 2 之后的状态:

ByteBuffer,位置 = 300,限制 = 1000,容量 = 1000
ByteBuffer,位置 = 300,限制 = 1000,容量 = 1000

假设我们现在要向缓冲区写入 300 个字节。缓冲区将如下所示:

ByteBuffer,位置 = 600,限制 = 1000,容量 = 1000
ByteBuffer,位置 = 600,限制 = 1000,容量 = 1000

如果我们现在使用flip()切换回读取模式,position将回到 0:

ByteBuffer,位置 = 0,限制 = 600,容量 = 1000
ByteBuffer,位置 = 0,限制 = 600,容量 = 1000

但是,现在我们将再次读取我们已经读取的前 200 个字节。

因此,这种方法是错误的。以下部分说明如何正确执行此操作。

使用 Buffer.compact() 切换到写入模式

相反,当切换到写入模式时,我们必须按以下步骤进行:

  • 我们计算剩余字节数:remaining = limit - position在示例中,结果为 100。
  • 我们将剩余的字节移到缓冲区的开头。
  • 我们将写入位置设置为左移字节的末尾,在示例中为 100。
  • 我们设置limit到缓冲区的末尾。

ByteBuffer 还为此提供了一个方便的方法:

代码语言:javascript
复制
 buffer.compact(); 

调用后compact()printMetrics()打印以下内容:

position = 100, limit = 1000, capacity = 1000

在图中,该compact()过程如下所示:

ByteBuffer,位置 = 100,限制 = 1000,容量 = 1000
ByteBuffer,位置 = 100,限制 = 1000,容量 = 1000

下一个循环

现在我们可以将接下来的 300 个字节写入缓冲区:

代码语言:javascript
复制
byte[] threes = new byte[300];
Arrays.fill(threes, (byte) 3);
buffer.put(threes);

printMetrics() 现在显示以下值:

position = 400, limit = 1000, capacity = 1000

写完三个之后,position向右移动了 300 个字节:

ByteBuffer,位置 = 400,限制 = 1000,容量 = 1000
ByteBuffer,位置 = 400,限制 = 1000,容量 = 1000

现在我们可以使用以下命令轻松切换回阅读模式flip()

代码语言:javascript
复制
 buffer.flip(); 

最后一次调用printMetrics()打印以下值:

position = 0, limit = 400, capacity = 1000

读取位置位于缓冲区的开头,该compact()方法将剩余的 100 个二进制移至该位置。所以我们现在可以准确地在我们之前停止的位置继续阅读。

ByteBuffer,位置 = 0,限制 = 400,容量 = 1000
ByteBuffer,位置 = 0,限制 = 400,容量 = 1000

概括

本文介绍了Java的功能ByteBuffer和它flip()compact()方法。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是 ByteBuffer,你需要它做什么?
  • 如何创建一个字节缓冲区
  • ByteBuffer 位置、限制和容量
  • ByteBuffer 读写周期
    • 使用 put() 写入 ByteBuffer
      • 使用 Buffer.flip() 切换到读取模式
        • 使用 get() 从 ByteBuffer 读取
          • 切换到写入模式 - 如何不这样做
            • 使用 Buffer.compact() 切换到写入模式
              • 下一个循环
              • 概括
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档