何时使用字节数组和何时使用字节缓冲区?

Posted

技术标签:

【中文标题】何时使用字节数组和何时使用字节缓冲区?【英文标题】:When to use byte array & when byte buffer? 【发布时间】:2011-07-09 19:28:12 【问题描述】:

字节数组和字节缓冲区有什么区别?另外,在什么情况下应该优先考虑一个?

[我的用例是用 java 开发的 Web 应用程序]。

【问题讨论】:

【参考方案1】:

实际上有很多方法可以处理字节。而且我同意选择最好的并不总是那么容易:

byte[] java.nio.ByteBuffer java.io.ByteArrayOutputStream(与其他流结合使用) java.util.BitSet

byte[] 只是一个原始数组,仅包含原始数据。因此,它没有方便的方法来构建或操作内容。

ByteBuffer 更像是一个构建器。它创建了一个byte[]。与数组不同,它有更方便的辅助方法。 (例如append(byte) 方法)。就usage 而言,这并不是那么简单。 (大多数教程太复杂或质量很差,但这个one 会让你有所收获。更进一步?然后阅读更多pitfalls。)

您可能会想说ByteBufferbyte[] 的作用,StringBuilderString 的作用。但是ByteBuffer 类有一个特定的区别/缺点。虽然看起来字节缓冲区在您添加元素时会自动调整大小,但ByteBuffer 实际上具有固定容量。当您实例化它时,您已经必须指定最大大小 缓冲区。

这就是为什么我经常喜欢使用ByteArrayOutputStream 的原因之一,因为它会自动调整大小,就像ArrayList 一样。 (它有一个toByteArray() 方法)。有时将其包装在DataOutputStream 中很实用。优点是您将有一些额外的方便调用(例如,writeShort(int),如果您需要写入 2 个字节。)

BitSet 在您想要执行位级操作时会派上用场。 您可以获取/设置单个位,它具有像 xor() 这样的逻辑运算符方法。 (toByteArray() 方法仅在 java 7 中引入。)

当然,根据您的需要,您可以将所有这些组合起来构建您的byte[]

【讨论】:

你的回答很有帮助【参考方案2】:

ByteBuffer 是新 IO 包 (nio) 的一部分,该包是为快速处理基于文件的数据而开发的。具体来说,Apache 是一个非常快速的 Web 服务器(用 C 语言编写),因为它从磁盘读取字节并将它们直接放到网络上,而无需通过各种缓冲区对它们进行混洗。它通过内存映射文件来做到这一点,而早期版本的 Java 没有。随着 nio 的出现,用 Java 编写与 Apache 一样快的 Web 服务器成为可能。当您想要非常快的文件到网络吞吐量时,您需要使用内存映射文件和 ByteBuffer。

数据库通常使用内存映射文件,但这种用法在 Java 中很少有效率。在 C/C++ 中,可以加载大量内存并将其转换为所需的类型数据。由于 Java 的安全模型,这通常是不可行的,因为您只能转换为某些本机类型,而这些转换效率不高。当您仅将字节作为纯字节数据处理时,ByteBuffer 效果最好——一旦您需要将它们转换为对象,其他 java io 类通常性能更好并且更易于使用。

如果您不处理内存映射文件,那么您实际上不需要为 ByteBuffer 操心——您通常会使用字节数组。如果您正在尝试构建一个 Web 服务器,以尽可能快地处理基于文件的原始字节数据,那么 ByteBuffer(特别是 MappedByteBuffer)是您最好的朋友。

【讨论】:

限制不是 Java 安全模型。正是 JVM 架构阻止您将字节转换为类型化数据。 安全模型也会影响 ByteBuffer 的可用性——至少在我几年前的测试中是这样。每次调用 ByteBuffer 类中的一个转换函数时,SecurityManager 代码都会被执行,这会减慢整个过程。这就是为什么常规的 java io 函数在读取 java 基本类型时通常更快的原因。这与 C 形成对比,在 C 中,使用强制转换的内存映射文件比使用 stdio 快得多。 查看代码,安全管理器调用似乎只发生在 DirectByteBuffer 的情况下。我认为这是因为该方法使用Unsafe【参考方案3】:

这两篇文章http://nadeausoftware.com/articles/2008/02/java_tip_how_read_files_quickly和http://evanjones.ca/software/java-bytebuffers.html可能对你有所帮助

【讨论】:

我无法重现第一个链接的结论,即 FileChannel 在读入字节 [] 方面比 FileInputStream 快。我怀疑由于他们使用长度为 100MB 的文件,他们实际上是从操作系统的磁盘缓存而不是硬盘驱动器本身对读取进行基准测试。这可以解释为什么他们的测试意味着 250MB/s 的带宽,这对于磁盘来说非常快。在我对一个 1.5GB 文件的测试中,两种方法都实现了 40MB/s 的吞吐量,这表明磁盘是瓶颈,而不是 CPU。当然,使用固态硬盘的里程可能会有所不同。 您可以通过让我们知道为什么这些链接可能会有所帮助来提高此答案的质量。仅链接的答案并不理想。

以上是关于何时使用字节数组和何时使用字节缓冲区?的主要内容,如果未能解决你的问题,请参考以下文章

使用数组和队列的方式解析字节

在 Java 中使用字节缓冲区创建数据包字节数组

使用 LINQ 在字节数组中搜索以特定字节开始/停止的所有子数组

如何读取和写入位到字节数组

从字节数组填充音频缓冲区并使用渲染回调播放

ByteBuffer 和字节数组