如何在Java字符(16位)中存储UTF-8字符(8位)时避免内存浪费.二合一?

ib8*_*b84 6 java memory byte utf-8 8-bit

我恐怕对一个相当过度饱和的话题的细节有疑问,我搜索了很多,但是找不到明确的答案 - 这个特别明显的 - 重要的,问题:

使用UTF-8将byte []转换为String时,每个字节(8位)变为由UTF-8编码的8位字符,但每个UTF-8字符在java中保存为16位字符.那是对的吗?如果是,这意味着,每个愚蠢的java字符只使用前8位,并消耗内存的两倍?这也是正确的吗?我想知道这种浪费行为是如何被接受的..

有没有一些技巧有一个8位的伪字符串?这实际上会减少内存消耗吗?或者,有没有办法在一个java 16bit字符中存储>两个<8位字符,以避免这种内存浪费?

感谢任何令人困惑的答案......

编辑:嗨,谢谢大家的回答.我知道UTF-8的可变长度属性.但是,由于我的源是8位的字节,我理解(显然是错误的)它只需要8位UTF-8字.UTF-8转换实际上是否会保存您在CLI上看到的"cat somebinary"时看到的奇怪符号?我认为UTF-8只是用于将每个可能的8位字的字节映射到UTF-8的一个特定的8位字.错误?我想过使用Base64,但它很糟糕,因为它只使用了7位..

重新制定的问题:是否有更智能的方法将字节转换为字符串?可能最喜欢的是将byte []转换为char [],但之后我仍然有16位字.

其他用例信息:

我正在调整Jedis(NoSQL Redis的Java客户端)作为hypergraphDB的"原始存储层".因此,jedis是另一个"数据库"的数据库.我的问题是我必须一直用byte []数据提供jedis,但在内部,> Redis <(实际服务器)只处理"二进制安全"字符串.由于Redis是用C语言编写的,因此char是8位长,AFAIK不是ASCIII,是7位.然而,在Jedis中,java世界,每个字符在内部都是16位长.我还不了解这段代码,但我想jedis然后将这个java 16位字符串转换为符合Redis的8位字符串(([here] [3]).它说它扩展了FilterOutputStream.我希望绕过它字节[] < - >字符串转换完全并使用Filteroutputstream ...?)

现在我想知道:如果我不得不一直互换byte []和String,数据量从非常小到可能非常大,那么每个8位字符在java中传递为16位并不会浪费大量内存?

Pet*_*rey 9

有没有一些技巧有一个8位的伪字符串?

是的,请确保您拥有最新版本的Java.;)

http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html

-XX:+ UseCompressedStrings对字符串使用byte [],可以表示为纯ASCII.(在Java 6 Update 21性能发布中引入)

编辑:此选项在Java 6更新22中不起作用,并且在Java 6更新24中默认不启用.注意:看起来此选项可能会使性能降低约10%.

以下程序

public static void main(String... args) throws IOException {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < 10000; i++)
        sb.append(i);

    for (int j = 0; j < 10; j++)
        test(sb, j >= 2);
}

private static void test(StringBuilder sb, boolean print) {
    List<String> strings = new ArrayList<String>();
    forceGC();
    long free = Runtime.getRuntime().freeMemory();

    long size = 0;
    for (int i = 0; i < 100; i++) {
        final String s = "" + sb + i;
        strings.add(s);
        size += s.length();
    }
    forceGC();
    long used = free - Runtime.getRuntime().freeMemory();
    if (print)
        System.out.println("Bytes per character is " + (double) used / size);
}

private static void forceGC() {
    try {
        System.gc();
        Thread.sleep(250);
        System.gc();
        Thread.sleep(250);
    } catch (InterruptedException e) {
        throw new AssertionError(e);
    }
}
Run Code Online (Sandbox Code Playgroud)

默认情况下打印此项

Bytes per character is 2.0013668655941212
Bytes per character is 2.0013668655941212
Bytes per character is 2.0013606946433575
Bytes per character is 2.0013668655941212
Run Code Online (Sandbox Code Playgroud)

有选项 -XX:+UseCompressedStrings

Bytes per character is 1.0014671435440285
Bytes per character is 1.0014671435440285
Bytes per character is 1.0014609725932648
Bytes per character is 1.0014671435440285
Run Code Online (Sandbox Code Playgroud)


Pis*_*3.0 5

实际上,您的UTF-8部分是错误的:UTF-8是一个可变长度的多字节编码,所以有效字符长度为1-4个字节(换句话说,一些UTF-8字符是8位,有些是16位,有些是24位,有些是32位).尽管1字节字符占用8位,但还有更多的多字节字符.如果你只有1个字节的字符,它只允许你总共有256个不同的字符(又名"扩展ASCII"); 这可能足以在英语(我用的90%天真瞎猜),但将尽快咬你的屁股,你甚至超越子集东西(见天真的词-英文,但不能刚写用ASCII).

因此,虽然UTF-16(Java使用)看起来很浪费,但实际上并非如此.无论如何,除非你是一个非常有限的嵌入式系统(在这种情况下,你用Java做什么?),试图削减字符串是毫无意义的微优化.

有关字符编码的更长时间的介绍,请参阅例如:http://www.joelonsoftware.com/articles/Unicode.html