ByteArray转byte[]的两种方式以及HeapByteBuffer&DirectByteBuffer

简介: 将ByteArray转byte[],大部分人第一时间会使用get函数

使用get函数获取


将ByteArray转byte[],大部分人第一时间会使用get函数


public ByteBuffer get(byte[] dst, int offset, int length) {
    checkBounds(offset, length, dst.length);
    if (length > remaining())
        throw new BufferUnderflowException();
    int end = offset + length;
    for (int i = offset; i < end; i++)
        dst[i] = get();
    return this;
}
public ByteBuffer get(byte[] dst) {
    return get(dst, 0, dst.length);
}
复制代码


这两个get函数,都是先创建byte[]数组并传入,然后将数据写入。 注意这里就需要知道创建的byte[]数组的长度,一般使用


int len = byteBuffer.limit() - byteBuffer.position();
复制代码


这里就涉及到ByteArray的几个属性

  • capacity:容量,不能改变,超过容量会报错
  • limit:第一个不可读写的位置,即最后一个数据位置的后一位。实际上就是数据长度,它不能超过容量。
  • position:当前的位置。

那么它们三个是如何变化的?看下面的图片


网络异常,图片无法展示
|


可以看到当我们写入数据的时候,position会移动。当我们写完数据准备读数据时,需要先调用flip将position移至开头,这样才能读出完整的数据。

但是我们也可以从中间开始读数据,所以我们要读出的数据长度是limit-position,而不仅仅是limit


使用array函数获取


但是ByteArray还有另外一个函数,之前很少注意到,那就array

public final byte[] array() {
    if (hb == null)
        throw new UnsupportedOperationException();
    if (isReadOnly)
        throw new ReadOnlyBufferException();
    return hb;
}
复制代码


这个直接得到一个byte[],那么直接使用这个不是更好么?

我们可以看到这个byte数组是ByteArray的一个属性hb,且这个hb有为null的时候。

那么这个hb是什么?我们看看源码


final byte[] hb;   // Non-null only for heap buffers
复制代码


可以看到只有在heap buffer中才不为空,那么什么是heap buffer?

这里就涉及到ByteArray的实现,通过代码可以看到ByteArray是一个抽象类,我们实际使用的都是它的实现类HeapByteBuffer和DirectByteBuffer。


HeapByteBuffer和DirectByteBuffer


我们创建ByteArray的时候会使用allocate函数,在ByteArray里有两个函数


public static ByteBuffer allocateDirect(int capacity) {
    // Android-changed: Android's DirectByteBuffers carry a MemoryRef.
    // return new DirectByteBuffer(capacity);
    DirectByteBuffer.MemoryRef memoryRef = new DirectByteBuffer.MemoryRef(capacity);
    return new DirectByteBuffer(capacity, memoryRef);
}
public static ByteBuffer allocate(int capacity) {
    if (capacity < 0)
        throw new IllegalArgumentException();
    return new HeapByteBuffer(capacity, capacity);
}
复制代码


可以看到这两个函数就是分别创建HeapByteBuffer和DirectByteBuffer

那么它们两个有什么区别?


DirectByteBuffer不是分配在堆上的,它不被GC直接管理(但Direct Buffer的JAVA对象是归GC管理的,只要GC回收了它的JAVA对象,操作系统才会释放Direct Buffer所申请的空间),它似乎给人感觉是“内核缓冲区(buffer in kernel)”。HeapByteBuffer则是分配在堆上的,或者我们可以简单理解为Heap Buffer就是byte[]数组的一种封装形式,查看JAVA源代码实现,HeapByteBuffer也的确是这样。 说白了就是HeapByteBuffer是在JVM堆内存中分配会被JVM管理回收,但是DirectByteBuffer是直接由系统内存进行分配,不被JVM管理。通过上面的区别看到:

1、创建和释放DirectByteBuffer的代价比HeapByteBuffer得要高,因为JVM堆中分配和释放内存肯定比系统分配和创建内存高效

2、因为平时的read/write,都会在I/O设备与应用程序空间之间经历一个“内核缓冲区”。 DirectByteBuffer就好比是“内核缓冲区”上的缓存,不直接受GC管理;而Heap Buffer就仅仅是byte[]字节数组的包装形式。因此把一个Direct Buffer写入一个Channel的速度要比把一个HeapByteBuffer写入一个Channel的速度要快。

所以这两个类操作起来各有好处,要视情况而定,一般如果是一个ByteBuffer经常被重用的话,就可以使用DirectByteBuffer对象。如果是需要经常释放和分配的地方用HeapByteBuffer对象。


从它们处理get或put数据上就可以看到差别。这里以get为例:


//HeapByteBuffer
public byte get() {
    return hb[ix(nextGetIndex())];
}
复制代码


HeapByteBuffer的get很简单,就是直接从hb中获取相应位置的数据


//DirectByteBuffer
public final byte get() {
    if (!memoryRef.isAccessible) {
        throw new IllegalStateException("buffer is inaccessible");
    }
    return get(ix(nextGetIndex()));
}
private byte get(long a) {
    return Memory.peekByte(a);
}
复制代码


DirectByteBuffer的get函数最终是使用Memory的peekByte来获取的。所以DirectByteBuffer的操作都是通过Memory进行处理的。


结论


所以我们知道HeapByteBuffer是分配在堆上的,本质上就是byte[],所以它的hb不为null,就是这个byte[]。 所以HeapByteBuffer可以直接使用array函数得到byte[]但是DirectByteBuffer就不行,因为它的hb是null的。所以DirectByteBuffer只能使用get函数获取byte[]

这里注意,在高版本(至少android 29,具体从哪个版本不清楚)上DirectByteBuffer的hb已经不是null了,且也有数据,但是这个数据是否是正常的数据还未进行调研,为了保险起见还是使用get函数安全一些


目录
相关文章
|
Java
java 读取文件 获取byte[]字节 并执行Gzip的压缩和解压
java 读取文件 获取byte[]字节 并执行Gzip的压缩和解压
125 0
|
5月前
|
Java Apache Maven
Java:commons-codec实现byte数组和16进制字符串转换
在上述代码中,`Hex.encodeHexString(bytes)`用于将byte数组转换为16进制字符串,`Hex.decodeHex(hexString)`用于将16进制字符串转换为byte数组。
108 0
|
5月前
|
Java Apache Maven
Java:commons-codec实现byte数组和16进制字符串转换
在上述代码中,`Hex.encodeHexString(bytes)`用于将byte数组转换为16进制字符串,`Hex.decodeHex(hexString)`用于将16进制字符串转换为byte数组。
133 0
|
存储 Java 计算机视觉
java 之byte
当涉及到处理数据时,Java 提供了多种数据类型,其中包括 `byte` 类型。在本文中,我们将深入探讨 Java 中的 `byte` 数据类型,了解它的特点、用途以及在编程中的实际应用。
|
Java
Java中 String与基本数据类型,包装类,char[],byte[]之间的转换
Java中 String与基本数据类型,包装类,char[],byte[]之间的转换
97 0
|
存储 Java
[java 基础知识] byte int 互转
[java 基础知识] byte int 互转
133 0
|
Dubbo 应用服务中间件
Dubbo使用Hessian2序列化时针对Byte类型出现java.lang.ClassCastException
Dubbo使用Hessian2序列化时针对Byte类型出现java.lang.ClassCastException
188 0
|
存储 Java
java基础类型中的char和byte的辨析及Unicode编码和UTF-8的区别
java基础类型中的char和byte的辨析及Unicode编码和UTF-8的区别
194 0
|
JavaScript Java 数据库
UTF-8 GBK UTF8 GB2312之间的区别和关系,Java中String和byte[]间的转换,byte 是怎样转为汉字,汉字转byte的;char与
UTF-8 GBK UTF8 GB2312之间的区别和关系,Java中String和byte[]间的转换,byte 是怎样转为汉字,汉字转byte的;char与
359 0
UTF-8 GBK UTF8 GB2312之间的区别和关系,Java中String和byte[]间的转换,byte 是怎样转为汉字,汉字转byte的;char与