如何扩展字节缓冲区的已分配内存

问题描述 投票:4回答:5

我有一个字节缓冲区,然后放入Ints,Chars等。因为我不知道我需要多少空间,所以我想动态增加字节缓冲区。如何做到这一点?

示例:-我有2个字节的字节缓冲区-我在字节缓冲区中添加了一个字符(字节缓冲区现在已满)-我想通过将字节缓冲区扩展为4个字节来向该字节缓冲区添加一个整数。我无法在开始时分配6个字节的字节缓冲区。

    ByteBuffer byteBuffer = ByteBuffer.allocate(2);

    byteBuffer.putChar('a');
    byteBuffer.putInt(1);

我对几分钟之内正在处理我这个问题的人印象深刻。非常感谢大家,也感谢Stackoverflow,这是一个很棒的平台!

所有人都问,我在做什么。所以我尝试在这里解释。我的用例:我有表示为javaobjects(javaclasses)的结构化数据,我想存储该数据并从中读取数据。阅读应该非常快。到目前为止,我所做的是:

  • java序列化并反序列化并将其存储在blob中->效果很好,但速度太慢。

  • 尝试了多个第3方串行器,例如kryo(非常好),但在我的系统中不可用case(android)。

我的新策略:-):我自己对班级进行外部化。为此,我想将我班级的所有数据按顺序构造为字节数组。这可能很慢。然后,我将字节数组存储到数据库中的blob中。读取时,我想一次读取字节数组(字节数组大约为10k)。 (我将有很多)。然后解析字节数组以提取结构化数据。

我认为使用字节缓冲区非常适合执行此操作,因为putX和readX之类的方法? (X为字符,浮点数,整数)

java bytebuffer
5个回答
1
投票

你不能。这是设计使然。您可以分配一个新的字节缓冲区,并将溢出的数据写入该缓冲区。您可以将ByteBuffers保留在LinkedList中(根据需要增加),甚至可以将旧的Buffers缓冲到磁盘上,如果内存不足以分配新的。如果每个ByteBuffer的大小相同,那么琐碎的方程式将使您可以像访问一个缓冲区一样访问它,但是您失去了使用切片或压缩的能力,或者无法使用其中之一进行任何酷操作。 :)

但是就像人们一遍又一遍地说,这取决于您的需要。


2
投票

恕我直言,最好的答案是从一开始就确保您有足够的空间。动态调整ByteBuffer的大小非常昂贵,而且速度慢得多。

ByteBuffer byteBuffer = ByteBuffer.allocate(6 /* or more */);

byteBuffer.putChar('a');
byteBuffer.putInt(1);

最简单的用于字符的缓冲区是StringBuilder。

StringBuilder sb = new StringBuilder();
sb.append('a');
sb.append('b');

您在上一次声明后如何添加新字符?

sb.append('n');

仅在完成时。

// if you need a ByteBuffer
ByteBuffer bb = ByteBuffer.wrap(sb.toString().getBytes(StandardCharsets.UTF_8));
// bb will have two bytes for 'a' and 'b'

但是,如果要追加到ByteBuffer(而不使用char),我建议使缓冲区大于所需的大小,因此无需调整其大小。如果您担心使用堆,则可以改用堆外。

// uses about 48 bytes of heap.
ByteBuffer bb = ByteBuffer.allocateDirect(1024*1024);

bb.putInt(1234);
bb.putDouble(1.111);
bb.putLong(12345678987654321L);

2
投票

使用字节缓冲区的问题是,您从固定大小开始。如果您知道要保留多少数据,那就太好了。例如,如果您正在读取输入,而您只想读取12个字节,则只需创建一个12个字节的数组。

如果您不知道字节缓冲区中将有多少数据,您可以做两件事:

  1. 如果您需要更快的速度,则只需根据将需要多少数据的预测来分配一个大型数组。例如,如果您正在读取一个大文件,并且想要快速读取,那么您应该使用一个非常大的字节缓冲区(取决于文件的大小,可能是几个MB)。

  2. 使用使用动态分配的结构。这是更好的解决方案,但速度较慢。如果您要分配非常大的数组,那么这将非常慢,但是不会浪费任何内存。如果您使用字节缓冲区并分配512 KB,但仅使用1KB,那将浪费大量的空间!使用动态分配的结构(LinkedList,堆栈,队列,树),您可以根据需要添加和删除元素

最终解决方案,完全不建议这样做,因为它不仅浪费大量内存,而且速度很慢。您可以在字节缓冲区中分配所需的空间,然后在需要更多内存时,创建一个新的缓冲区并复制数据。这是您要尝试执行的操作,效率很低。

在我看来,Peter Lawrey的答案是解决该问题的好方法,仅因为您可以轻松地从StringBuilder转到字节数组。这具有您需要的所有效率和速度。


0
投票

我找到了我的用例的解决方案,在这里我将简短描述。我接受了米克尔(Mikkel)的答案,因为对于这个问题,我认为这是正确的答案,约翰·门托宁(John mentoinate)也是如此。同时还要感谢彼得的出色解释,我可以从中学习。

我使用DataInputStream,这对我来说很方便。我使用DataInputStream和诸如putInt,PutBoolean,PutString之类的便捷方法,基于Java对象创建了一个数据流。然后我得到DataInputStream的binaryarray并将其作为blob存储到我的数据库中。

阅读正好与DataInputStream相反。

通过这种方式,与我的javaobject的javadeserialization相比,读取对象的性能提高了500%,而存储使用量则减少了25%。


0
投票

如果您将缓冲区实例保持在变量中,则可以。例如这样的(kotlin):

fun ShortBuffer?.clone(addLimit: Int = 0): ShortBuffer? {
    if (this == null) {
        return ShortBuffer.allocate(addLimit)
    } else {
        val copy = ShortBuffer.allocate(this.limit() + addLimit)
        copy.put(this)
        return copy
    }
}

fun ShortBuffer?.safePut(shortArray: ShortArray?): ShortBuffer? {
    if (this == null) {
        return null
    } else if (shortArray == null) {
        return this
    } else if (this.remaining() < shortArray.size) {
        val ret = clone(shortArray.size-this.remaining())
        ret?.put(shortArray)
        return ret
    } else {
        this.put(shortArray)
        return this
    }
}

用法:

var buff = ShortBuffer.allocate(0)
buff = buff.safePut(shortArrayOf(1,2,3))
© www.soinside.com 2019 - 2024. All rights reserved.