Java中字符串的最大长度 - 调用length()方法

167
在Java中,使用length()方法调用时,String对象的最大大小是多少?
我知道length()方法将一个String的大小返回为一个char []

9
虽然 String 的长度理论上可以达到 Integer.MAX_VALUE,但是在源代码中,字符串字面量的长度似乎仅限于 65535 bytes 的 UTF-8 数据。 - 200_success
7个回答

184
考虑到String类的length方法返回一个int,该方法返回的最大长度将是Integer.MAX_VALUE,即2^31 - 1(约为20亿)。
在数组的长度和索引方面(例如char[],这可能是实现String的内部数据表示方式),The Java Language Specification, Java SE 7 Edition第10章:数组中如下所述:
数组中包含的变量没有名称,而是通过使用非负整数索引值的数组访问表达式进行引用。这些变量称为数组的组件。如果一个数组有n个组件,我们称n为数组的长度;数组的组件使用从0到n-1的整数索引进行引用。此外,如第10.4节所述,索引必须是int值:数组必须由int值索引。因此,似乎限制确实是2^31-1,因为这是非负int值的最大值。但是,可能会出现其他限制,例如可分配数组的最大大小。

32
Integer.MAX_VALUE 实际上是 2^31-1。 :) - Michael Myers
5
我刚刚尝试在一个Java的hello world程序中定义了一个长于65546的字符串字面量。javac报错该字面量太长:javac HelloWorld.java 2>&1|head -c 80 HelloWorld.java:3: constant string too long - dlamblin
3
这句话听起来像是javac对于String字面值(而不是String对象)的限制,因为我在Java语言规范和JVM规范中找不到任何关于String字面值大小限制的参考资料。我试着创建一个超过10万个字符的String字面值,Eclipse编译器没有问题编译它。(运行程序后能够显示该字面值具有超过10万个字符的String.length)。 - coobird
3
三年前的事了,所以我得想一下。;) 我的意思是,要构建一个最大尺寸的字符串,你需要非常多的内存,可能超过你拥有的内存。你需要每个字符两个字节,约为 4GB,但你需要使用 StringBuilder 或 char[] 构建它,这意味着你需要每个字符另外两个字节来创建它,也就是说,你需要另外约 4GB(至少暂时)。 - Peter Lawrey
1
我刚刚尝试了当前的Oracle JVM实现,并得到了一个最大的char[]长度为Integer.MAX_VALUE-2。由于String使用char数组,所以当前的实际限制略低于理论限制。 - Holger
显示剩余8条评论

31

java.io.DataInput.readUTF()java.io.DataOutput.writeUTF(String)指出,一个String对象由两个字节的长度信息和字符串中每个字符的修改后UTF-8编码表示组成。因此,在使用DataInputDataOutput时,字符串的长度受到字符串修改后UTF-8编码所需的字节数的限制。

此外,在Java虚拟机规范中找到的CONSTANT_Utf8_info的规范定义如下结构。

CONSTANT_Utf8_info {
    u1 tag;
    u2 length;
    u1 bytes[length];
}

'length'的大小为两个字节

某些方法(例如String.length())的返回类型为int并不总是意味着其允许的最大值为Integer.MAX_VALUE。相反,在大多数情况下,选择int只是为了提高性能。Java语言规范指出,在计算之前,长度小于int的整数将被转换为int(如果我记得正确),这也是在没有特殊原因时选择int的原因之一。

编译时的最大长度最多为65536个字节。请再次注意,长度是修改过的UTF-8表示的字节数,而不是String对象中字符的数量。

String对象在运行时可能具有更多的字符。但是,如果想要使用具有DataInputDataOutput接口的String对象,则最好避免使用太长的String对象。当我实现DataInput.readUTF()DataOutput.writeUTF(String)的Objective-C等效项时,发现了这种限制。


1
这应该是默认答案。 - Nick
这是正确的答案。特别是关于CONSTANT_Utf8_info规范的那部分 :) - Max Coplan

20

由于数组必须使用整数作为索引,因此数组的最大长度为Integer.MAX_INT(231-1,或2 147 483 647)。当然,这是假设您有足够的内存来容纳该大小的数组。


19

我有一台2010年的 iMac,配备了8GB内存,运行Eclipse Neon.2 Release (4.6.2)和Java 1.8.0_25。使用VM参数-Xmx6g,我运行了以下代码:

StringBuilder sb = new StringBuilder();
for (int i = 0; i < Integer.MAX_VALUE; i++) {
    try {
        sb.append('a');
    } catch (Throwable e) {
        System.out.println(i);
        break;
    }
}
System.out.println(sb.toString().length());

这会打印:

Requested array size exceeds VM limit
1207959550

所以,似乎最大数组大小为约1,207,959,549。然后我意识到我们实际上不关心Java是否用尽了内存:我们只是在寻找最大的数组大小(似乎是在某个地方定义的常量)。那么:

for (int i = 0; i < 1_000; i++) {
    try {
        char[] array = new char[Integer.MAX_VALUE - i];
        Arrays.fill(array, 'a');
        String string = new String(array);
        System.out.println(string.length());
    } catch (Throwable e) {
        System.out.println(e.getMessage());
        System.out.println("Last: " + (Integer.MAX_VALUE - i));
        System.out.println("Last: " + i);
    }
}

打印出:

Requested array size exceeds VM limit
Last: 2147483647
Last: 0
Requested array size exceeds VM limit
Last: 2147483646
Last: 1
Java heap space
Last: 2147483645
Last: 2

因此,似乎最大值为Integer.MAX_VALUE - 2,即(2^31) - 3。

顺便说一句,我不确定为什么我的StringBuilder在达到1207959550时达到上限,而我的char[]在(2^31)-3时达到上限。看起来,AbstractStringBuilder会将其内部的char[]大小加倍以进行扩展,因此可能会导致该问题。


5
一个非常有用的实际处理问题的方法。 - Pavlo Maistrenko

6

显然它绑定到一个整数,该整数为0x7FFFFFFF(2147483647)。


5

String类的length()方法的返回类型是int

public int length()

参考http://docs.oracle.com/javase/7/docs/api/java/lang/String.html#length()

因此,int的最大值是2147483647

String在内部被视为字符数组,所以索引是在最大范围内进行的。这意味着我们无法引用第2147483648个成员。因此,在Java中,String的最大长度为2147483647。

在Java中,基本数据类型int占用4字节(32位)。由于1位(MSB)用作符号位,所以范围限制在-2^31到2^31-1(-2147483648至2147483647)。我们不能使用负值进行索引。因此,我们可以使用的范围从0到2147483647。


2

正如Takahiko Kawasaki的回答中所提到的,Java使用修改后的UTF-8来表示Unicode字符串,并在JVM规范的CONSTANT_UTF8_info结构体中,分配2个字节用于字符串长度(而不是字符数)。
为了扩展这个答案,ASM jvm bytecode库的putUTF8方法包含如下内容:

public ByteVector putUTF8(final String stringValue) {
    int charLength = stringValue.length();
    if (charLength > 65535) {   
   // If no. of characters> 65535, than however UTF-8 encoded length, wont fit in 2 bytes.
      throw new IllegalArgumentException("UTF8 string too large");
    }
    for (int i = 0; i < charLength; ++i) {
      char charValue = stringValue.charAt(i);
      if (charValue >= '\u0001' && charValue <= '\u007F') {
        // Unicode code-point encoding in utf-8 fits in 1 byte.
        currentData[currentLength++] = (byte) charValue;
      } else {
        // doesnt fit in 1 byte.
        length = currentLength;
        return encodeUtf8(stringValue, i, 65535);
      }
    }
    ...
}

但是当代码点映射 > 1byte 时,它会调用 encodeUTF8 方法:

final ByteVector encodeUtf8(final String stringValue, final int offset, final int maxByteLength /*= 65535 */) {
    int charLength = stringValue.length();
    int byteLength = offset;
    for (int i = offset; i < charLength; ++i) {
      char charValue = stringValue.charAt(i);
      if (charValue >= 0x0001 && charValue <= 0x007F) {
        byteLength++;
      } else if (charValue <= 0x07FF) {
        byteLength += 2;
      } else {
        byteLength += 3;
      }
    }
   ...
}

在这种情况下,最大字符串长度是65535字节,即utf-8编码长度,而不是字符数。
您可以从上面的utf8结构链接中找到JVM的修改后Unicode代码点范围。


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