一个ByteBuffer中编码的字符串长度是多少?

9
byte[] byteArray = Charset.forName("UTF-8").encode("hello world").array();
System.out.println(byteArray.length);

为什么上面这行代码打印的是12,而不是11呢?

我怀疑这个问题的解决方法是,在使用ByteBuffer.array方法之前仔细阅读文档,并避免使用该方法。 - Hot Licks
我有点惊讶你可以直接访问后备数组,而不是被迫使用get和put方法。 - azurefrog
3个回答

11
数组的长度是ByteBuffer容量大小,这个大小是由编码字符数生成的,但并不等于编码字符数。让我们看一下如何为ByteBuffer分配内存...
如果你深入了解encode()方法,你会发现CharsetEncoder#encode(CharBuffer)的实现如下:
public final ByteBuffer encode(CharBuffer in)
    throws CharacterCodingException
{
    int n = (int)(in.remaining() * averageBytesPerChar());
    ByteBuffer out = ByteBuffer.allocate(n);
    ...
根据我的调试器显示,UTF_8$EncoderaverageBytesPerChar1.1,输入的String11个字符。11 * 1.1 = 12.1,在计算时代码将总数强制转换成int类型,因此ByteBuffer的结果大小为12

5
哇,我想知道是谁提出了这个平均数,以及他们使用了哪个数据集。在某种程度上,这让我想起一个笑话,讲的是一个统计学家把头伸进烤箱里,脚放进冰柜里,然后自称平均而言感觉很舒服。 - Ilmari Karonen

2
因为它返回一个ByteBuffer,这是缓冲区的容量(实际上由于可能进行切片而不是缓冲区使用的字节数)。这有点像malloc(10)可以自由地返回32字节内存。
System.out.println(Charset.forName("UTF-8").encode("hello world").limit());

这是11(如预期)。


我不这么认为。如果你创建了一个空数组,它会默认为某些值,可能比赋值后使用的字节数还要高,但在这种情况下,复制构造函数将被调用,我希望该数组被初始化为传入的字符数。 - ventsyv
当您创建一个数组时,.length将是您要求的长度(这在Java规范中有说明)。在这种情况下,encode()并没有说明它会做什么,只是返回一个带有您请求的编码字符的ByteBuffer。其他人深入研究了它,发现这是一项实现细节,因此这种行为甚至会在JVM版本和实现之间有所不同。 - David Ehrmann
好的,明白了。 - ventsyv

0
import java.nio.charset.*;
public class ByteArrayTest {
    public static void main(String[] args) {
        String theString = "hello world";
        System.out.println(theString.length());
        byte[] byteArray = Charset.forName("UTF-8").encode(theString).array();
        System.out.println(byteArray.length);
        for (int i = 0; i < byteArray.length; i++) {
            System.out.println("Byte " + i + " = " + byteArray[i]);
        }
    }
}

结果:

C:\JavaTools>java ByteArrayTest
11
12
Byte 0 = 104
Byte 1 = 101
Byte 2 = 108
Byte 3 = 108
Byte 4 = 111
Byte 5 = 32
Byte 6 = 119
Byte 7 = 111
Byte 8 = 114
Byte 9 = 108
Byte 10 = 100
Byte 11 = 0

该数组以空字符结尾,就像任何良好的C字符串一样。

(但显然真正的原因是不稳定的方法array。除非非常小心,否则它可能不应在“生产”代码中使用。)


3
我认为末尾的0字节并不完全是空终止符,更多的是未使用的缓冲区空间。 - Andreas
@Andreas - 是的,你可能是对的-- array 是一个半虚假的操作,它返回 ByteBuffer 的内部缓冲区,因此无法确定它的大小。 - Hot Licks
@Andreas:我想Java运行时是用C实现的,所以字符串被偷偷地以null结尾可能很方便。 :-) - Harry Johnston

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接