空间有效的长表示

时间:2011-02-16 22:31:16

标签: java compression

我想在Java中使用long值,并将其转换为字节数组。

但是,我希望小值的表示很小,所以如果值小于127,那么它只需要一个字节。

编码和解码算法应该非常有效。

我确定已经完成但我找不到任何示例代码,任何人都有任何指针?

5 个答案:

答案 0 :(得分:4)

您可以使用停止位编码,例如

public static void writeLong(OutputStream out, long value) throws IOException {
   while(value < 0 || value > 127) {
        out.write((byte) (0x80 | (value & 0x7F)));
        value = value >>> 7;
   }
   out.write((byte) value);
}

public static long readLong(InputStream in) throws IOException {
   int shift = 0;
   long b;
   long value = 0;
   while((b = in.read()) >= 0) {
      value += (b & 0x7f) << shift;
      shift += 7;
      if ((b & 0x80) == 0) return value;
   }
   throw new EOFException();
}

这是一种快速压缩形式,但所有压缩都需要付出代价。 (但是,如果您的带宽有限,那么传输速度可能会更快并且值得花费)

BTW:值0到127使用一个byte。您也可以对shortint值使用相同的例程。

编辑:在此之后你仍然可以使用通用压缩,它可以小于不使用它。

public static void main(String... args) throws IOException {
    long[] sequence = new long[1024];
    Random rand = new Random(1);
    for (int i = 0; i < sequence.length; i+=2) {
        sequence[i] = (long) Math.pow(2, rand.nextDouble() * rand.nextDouble() * 61);
        // some pattern.
        sequence[i+1] = sequence[i] / 2;
    }
    testDeflator(sequence);
    testStopBit(sequence);
    testStopBitDeflator(sequence);
}

private static void testDeflator(long[] sequence) throws IOException {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    DataOutputStream dos = new DataOutputStream(new DeflaterOutputStream(baos));
    for (long l : sequence)
        dos.writeLong(l);
    dos.close();
    System.out.println("Deflator used " + baos.toByteArray().length);
}


private static void testStopBit(long[] sequence) throws IOException {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    for (long l : sequence)
        writeLong(baos, l);
    baos.close();
    System.out.println("Stop bit used " + baos.toByteArray().length);
}

private static void testStopBitDeflator(long[] sequence) throws IOException {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    DataOutputStream dos = new DataOutputStream(new DeflaterOutputStream(baos));
    for (long l : sequence)
        writeLong(dos, l);
    dos.close();
    System.out.println("Stop bit & Deflator used " + baos.toByteArray().length);
}

public static void writeLong(OutputStream out, long value) throws IOException {
    while (value < 0 || value > 127) {
        out.write((byte) (0x80 | (value & 0x7F)));
        value = value >>> 7;
    }
    out.write((byte) value);
}

打印

Deflator used 3492
Stop bit used 2724
Stop bit & Deflator used 2615

最有效的方法在很大程度上取决于您发送的数据。例如如果您的数据是真正随机的,那么您使用的任何压缩技术都只会使数据更大。

Deflator是GZip输出的精简版本(减去标题和CRC32)

答案 1 :(得分:2)

简单地使用GZipOutputStream - 像GZip这样的熵编码基本上完全按照你描述的方式进行,只是一般。

修改 只是为了确定:你是否意识到对于小数字只使用1个字节的可变长度编码对于大多数大数字必须使用超过8个字节?除非您知道自己的数量远小于大数字,否则最终可能会增加数据的整体大小。而GZIP适应您的实际数据集,并且可以压缩以不同方式偏斜的数据集。

答案 2 :(得分:1)

请参阅C#中的Read7BitEncodedInt。 (这是相同的概念。)

答案 3 :(得分:0)

如果要存储具有不同长度的long值,那么您将需要一个分隔符,否则您无法确定哪个字节属于哪个长值...并且分隔符将添加额外的字节数据......

如果您正在寻找一个快速库来存储长值(每个64Bit),我建议colt 快。

答案 4 :(得分:0)

(我可能会向某些人陈述这些显而易见的......但是这里有。)

如果您要在某些外部序列化中减小long个值,请继续。

但是,如果您尝试在Java程序中保存内存,则可能会浪费您的时间。 Java中byte[]的最小表示是2或3个32位字。这是一个长度为零的字节数组。对于任何大于零的数组长度,添加一些32位字的倍数。然后,您必须允许至少1个32位字来保存对byte[]对象的引用。

如果你添加它,至少需要4个单词才能代表long以外的任何0L作为byte[]

如果您要在单个long中表示多个byte[]值,那么您将获得任何保存的唯一情况。在你可能收支平衡之前,你需要至少3 long个值,即使这样,如果你的价值平均来说太大,你也会失败。