Comprimento máximo da string em Java - chamando o método length ()

150

Em Java , qual é o tamanho máximo que um Stringobjeto pode ter, referindo-se à length()chamada do método?

Eu sei que length()retornam o tamanho de a Stringcomo a char [];

taichi
fonte
5
Enquanto o comprimento de a Stringé teoricamente Integer.MAX_VALUE, o comprimento de uma string literal na origem parece estar limitado a apenas 65535 bytes de dados UTF-8.
200_success

Respostas:

169

Considerando que o método da Stringclasse lengthretorna um int, o comprimento máximo que seria retornado pelo método seria Integer.MAX_VALUE, que é 2^31 - 1(ou aproximadamente 2 bilhões).

Em termos de tamanho e indexação de matrizes (como char[]provavelmente a maneira como a representação de dados interna é implementada para Strings), o Capítulo 10: Matrizes da especificação da linguagem Java, o Java SE 7 Edition diz o seguinte:

As variáveis ​​contidas em uma matriz não têm nomes; em vez disso, eles são referenciados por expressões de acesso à matriz que usam valores de índice inteiro não negativo. Essas variáveis ​​são chamadas de componentes da matriz. Se uma matriz tem ncomponentes, dizemos que né o comprimento da matriz; os componentes da matriz são referenciados usando índices inteiros de 0até n - 1, inclusive.

Além disso, a indexação deve ser por intvalores, conforme mencionado na Seção 10.4 :

Matrizes devem ser indexadas por intvalores;

Portanto, parece que o limite é realmente 2^31 - 1, pois esse é o valor máximo para um intvalor não negativo .

No entanto, provavelmente haverá outras limitações, como o tamanho máximo alocável para uma matriz.

coobird
fonte
26
Integer.MAX_VALUE é 2 ^ 31-1, na verdade. :)
Michael Myers
1
Ótima resposta, cara! Dei uma olhada no código-fonte String.java e está certo, 'count' é a variável int que retorna o comprimento da matriz char, e a matriz char é armazenada na variável 'value' (como char []). que o tamanho da String pode estar em torno de 2 GB. Obviamente, pode haver limitações para alocar esse tamanho de memória. Obrigado!
taichi
5
Eu apenas tentei definir um literal cadeia de caracteres em um programa java Olá mundo que era mais do que 65546. javacdá um erro sobre esse ser literal muito longo:javac HelloWorld.java 2>&1|head -c 80 HelloWorld.java:3: constant string too long
dlamblin
2
@ dlamblin: Isso soa como uma limitação javacpara String literais (não Stringobjetos), já que não consigo encontrar nenhuma referência aos limites de tamanho para Stringliterais na Java Language Specification e na JVM Specification. Tentei criar um Stringliteral com mais de 100.000 caracteres e o compilador Eclipse não teve problemas ao compilá-lo. (E, correndo o programa foi capaz de mostrar que o literal teve uma String.lengthmaior do que 100.000.)
coobird
3
@ Premraj Foi há três anos, então eu tive que pensar sobre isso. ;) O que eu quis dizer foi; Para criar uma cadeia de tamanho máximo, você precisa de muita memória, possivelmente mais do que você possui. Você precisa de dois bytes por caractere ~ 4 GB, mas precisa construí-lo a partir de um StringBuilder ou char [], o que significa que você precisa de mais dois bytes por caractere para criá-lo em primeiro lugar, ou seja, outros ~ 4 GB (pelo menos temporariamente)
Peter Lawrey
25

java.io.DataInput.readUTF()e java.io.DataOutput.writeUTF(String)diga que um Stringobjeto é representado por dois bytes de informações de comprimento e pela representação UTF-8 modificada de cada caractere na sequência. Isso conclui que o comprimento da String é limitado pelo número de bytes da representação UTF-8 modificada da string quando usada com DataInpute DataOutput.

Além disso, a especificaçãoCONSTANT_Utf8_info encontrada na especificação da máquina virtual Java define a estrutura da seguinte maneira.

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

Você pode achar que o tamanho do 'comprimento' é de dois bytes .

Que o tipo de retorno de um determinado método (por exemplo String.length()) seja intnem sempre significa que seu valor máximo permitido é Integer.MAX_VALUE. Em vez disso, na maioria dos casos, inté escolhido apenas por razões de desempenho. A especificação da linguagem Java diz que números inteiros cujo tamanho é menor que o de intsão convertidos para intantes do cálculo (se minha memória me servir corretamente) e é uma das razões para escolher intquando não há uma razão especial.

O comprimento máximo no tempo de compilação é no máximo 65536. Observe novamente que o comprimento é o número de bytes da representação UTF-8 modificada , não o número de caracteres em um Stringobjeto.

Stringos objetos podem ter muito mais caracteres em tempo de execução. No entanto, se você deseja usar Stringobjetos com DataInpute DataOutputinterfaces, é melhor evitar o uso de Stringobjetos muito longos . Encontrei essa limitação ao implementar os equivalentes Objective-C de DataInput.readUTF()e DataOutput.writeUTF(String).

Takahiko Kawasaki
fonte
1
Essa deve ser a resposta padrão.
Nick
20

Como as matrizes devem ser indexadas com números inteiros, o comprimento máximo de uma matriz é Integer.MAX_INT(2 31 -1 ou 2 147 483 647). Isso pressupõe que você tenha memória suficiente para armazenar uma matriz desse tamanho, é claro.

Michael Myers
fonte
9

Eu tenho um iMac 2010 com 8 GB de RAM, executando o Eclipse Neon.2 Release (4.6.2) com Java 1.8.0_25. Com o argumento da VM -Xmx6g, executei o seguinte código:

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());

Isso imprime:

Requested array size exceeds VM limit
1207959550

Portanto, parece que o tamanho máximo da matriz é ~ 1.207.959.549. Então percebi que na verdade não nos importamos se o Java ficar sem memória: estamos apenas procurando o tamanho máximo da matriz (que parece ser uma constante definida em algum lugar). Assim:

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);
    }
}

Que imprime:

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

Então, parece que o máximo é Inteiro.MAX_VALUE - 2 ou (2 ^ 31) - 3

PS: Não sei por que meu StringBuilderlimite máximo foi atingido 1207959550enquanto meu char[]limite máximo atingido (2 ^ 31) -3. Parece que AbstractStringBuilderdobra o tamanho de seu interno char[]para cultivá-lo, o que provavelmente causa o problema.

Dantiston
fonte
1
Um tratamento prático muito útil da questão
Pavlo Maistrenko 07/07
5

aparentemente está vinculado a um int, que é 0x7FFFFFFF (2147483647).

Francis
fonte
4

O tipo Return do método length () da classe String é int .

public int length ()

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

Portanto, o valor máximo de int é 2147483647 .

String é considerada como matriz de char internamente, portanto, a indexação é feita dentro do intervalo máximo. Isso significa que não podemos indexar o membro 2147483648. Portanto, o comprimento máximo de String em java é 2147483647.

O tipo de dados primitivo int é de 4 bytes (32 bits) em java. Como 1 bit (MSB) é usado como um bit de sinal , o intervalo é restrito dentro de -2 ^ 31 a 2 ^ 31-1 (-2147483648 a 2147483647). Não podemos usar valores negativos para indexação. Portanto, obviamente, o intervalo que podemos usar é de 0 a 2147483647.

Shanmugavel
fonte
0

Como mencionado na resposta de Takahiko Kawasaki , java representa seqüências de caracteres Unicode na forma de UTF-8 modificado e na estrutura CONSTANT_UTF8_info da JVM-Spec , 2 bytes são alocados no comprimento (e não no número de caracteres de String).
Para estender a resposta, o método da biblioteca ASM jvm bytecode contém: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);
      }
    }
    ...
}

Mas quando o mapeamento de ponto de código> 1byte, ele chama o encodeUTF8método:

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;
      }
    }
   ...
}

Nesse sentido, o comprimento máximo da string é 65535 bytes, ou seja, o comprimento da codificação utf-8. e não charconte
Você pode encontrar o intervalo de ponto de código Unicode modificado da JVM, no link struct utf8 acima.

DHS
fonte