如何扩展bytebuffer的已分配内存

时间:2014-04-22 16:03:50

标签: java bytebuffer

我有一个bytebuffer,我把Ints,Chars等。 因为我不知道我需要多少空间我想动态增长bytebuffer。怎么办呢?

示例: - 我有一个2字节的Bytebuffer - 我在bytebuffer中添加了一个字符(bytebuffer现已满了) - 我喜欢通过扩展4个字节的bytebuffer来向bytebuffer添加一个Integer。我无法在开始时为我的bytebuffer分配6个字节。

    ByteBuffer byteBuffer = ByteBuffer.allocate(2);

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

我印象非常深刻,有多少人正在处理我的问题,这个问题只有几分钟的时间。非常感谢大家,也感谢Stackoverflow,这是一个伟大的Plattform!

你们都问,我在做什么。所以我试着在这里解释一下。 我的用例: 我有结构化的数据表示为javaobjects(javaclasses),我想存储和读取数据库。阅读应该非常快。 到目前为止我做了什么:

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

  • 尝试了几个第三方序列化程序,如kryo(这是非常好的),但在我的用途中无法使用 case(android)。

我的新策略:-): 我自己外化我的班级。为此,我想将我的类顺序的整个数据构造为一个字节数组。这可能很慢。然后我将bytearray存储到我的db中的blob。在阅读时我想立刻读取bytearray(bytearray大约是10k)。 (我会有很多)。然后解析bytearray以提取结构化数据。

我认为使用bytebuffer是理想的,因为像putX和readX这样的方法? (X表示字符,浮点数,整数)

5 个答案:

答案 0 :(得分:1)

恕我直言,最好的答案是确保从一开始就有足够的空间。让你的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(而不是使用字符),我建议将缓冲区设置为比您需要的更大,因此您永远不需要调整它。如果您担心使用堆,则可以使用off heap。

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

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

答案 1 :(得分:1)

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

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

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

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

  3. 最终解决方案,根本不推荐,因为它不仅浪费了大量内存,而且速度也很慢。您可以在字节缓冲区中分配所需的空间,然后在需要更多内存时,创建一个新缓冲区并复制数据。这是你正在尝试做的事情,效率很低。

    在我的观点中,Peter Lawrey的答案是解决这个问题的一个很棒的解决方案,因为您可以轻松地从StringBuilder转到字节数组。这具有您需要的所有效率和速度。

答案 2 :(得分:0)

你做不到。这是设计的。您可以分配一个新的字节缓冲区并将溢出的数据写入该缓冲区。你可以将ByteBuffers保存在LinkedList中(它将根据需要增长),如果你的内存不足以分配新的,你甚至可以将旧的那些假脱机到磁盘。如果每个ByteBuffer都是相同的大小,那么简单的方程式就可以让你像访问一个缓冲区一样访问它,但是你失去了使用切片或压缩的能力,或者你可以用其中一个做的任何很酷的事情。 :)

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

答案 3 :(得分:0)

我为我的用例找到了一个解决方案,我想在此简要介绍一下。 我接受了Mikkel的答案,因为关于这个问题,我想这是正确的答案,John也是这个答案。还要感谢彼得的重要解释,我可以借鉴。

我使用DataInputStream,这对我的情况很方便。 我基于带有DataInputStream的Javaobject和putInt,PutBoolean,PutString等方便的方法创建了一个数据流。然后我得到DataInputStream的二进制数据amd将它作为blob存储到我的数据库中。

阅读完全是DataInputStream的对立方式。

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

答案 4 :(得分: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.array())
        return copy
    }
}

fun ShortBuffer?.safePut(shortArray: ShortArray?): ShortBuffer? {
    if (this == null) {
        if(shortArray == null) {
            return null
        } else {
            val ret = ShortBuffer.allocate(shortArray.size)
            ret.put(shortArray)
            return ret
        }
    } 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))