Longitud máxima de cadena en Java: método de longitud de llamada ()

150

En Java , ¿cuál es el tamaño máximo Stringque puede tener un objeto, en referencia a la length()llamada al método?

Sé que length()devuelven el tamaño de a Stringcomo a char [];

taichi
fuente
55
Si bien la longitud de a Stringes teóricamente Integer.MAX_VALUE, la longitud de un literal de cadena en la fuente parece estar limitada a solo 65535 bytes de datos UTF-8.
200_success

Respuestas:

169

Teniendo en cuenta que el método de la Stringclase lengthdevuelve un int, la longitud máxima que devolvería el método sería Integer.MAX_VALUE, que es 2^31 - 1(o aproximadamente 2 mil millones).

En términos de longitudes e indexación de matrices, (como char[], probablemente, la forma en que se implementa la representación de datos internos para Strings), el Capítulo 10: Matrices de la especificación del lenguaje Java, Java SE 7 Edition dice lo siguiente:

Las variables contenidas en una matriz no tienen nombres; en su lugar, se hace referencia a ellas mediante expresiones de acceso a matriz que usan valores de índice de enteros no negativos. Estas variables se denominan componentes de la matriz. Si una matriz tiene ncomponentes, decimos que nes la longitud de la matriz; Se hace referencia a los componentes de la matriz utilizando índices enteros desde 0hasta n - 1, inclusive.

Además, la indexación debe ser por intvalores, como se menciona en la Sección 10.4 :

Las matrices deben indexarse ​​por intvalores;

Por lo tanto, parece que el límite es 2^31 - 1, ya que ese es el valor máximo para un intvalor no negativo .

Sin embargo, probablemente habrá otras limitaciones, como el tamaño máximo asignable para una matriz.

Coobird
fuente
26
Integer.MAX_VALUE es 2 ^ 31-1, en realidad. :)
Michael Myers
1
Gran respuesta hombre! Eché un vistazo al código fuente String.java y está bien, 'contar' es la variable int que devuelve la longitud de la matriz de caracteres, y la matriz de caracteres se almacena en la variable 'valor' (como char []) Significa que el tamaño de la cadena podría ser de alrededor de 2 GB. Por supuesto, podría haber limitaciones para asignar ese tamaño de memoria. ¡Gracias!
taichi
55
Acabo de intentar definir un literal de cadena en un programa java de hello world que era más largo que 65546. javacda un error acerca de que ese literal es demasiado largo:javac HelloWorld.java 2>&1|head -c 80 HelloWorld.java:3: constant string too long
dlamblin
2
@dlamblin: Eso suena como una limitación de la javacde String los literales (no Stringobjetos), como no puedo encontrar ninguna referencia a los límites de tamaño de Stringliterales en la especificación del lenguaje Java y JVM especificación. Intenté hacer un Stringliteral que tuviera más de 100,000 caracteres, y el compilador de Eclipse no tuvo problemas para compilarlo. (Y ejecutar el programa fue capaz de mostrar que el literal tenía String.lengthmás de 100,000)
Coobird
3
@Premraj Fue hace tres años, así que tuve que pensarlo. ;) Lo que quería decir; para construir una cadena de tamaño máximo necesita mucha memoria, posiblemente más de la que tiene de todos modos. Necesita dos bytes por carácter ~ 4GB, pero necesita construir esto desde un StringBuilder o char [], lo que significa que necesita otros dos bytes por carácter para crearlo, es decir, otros ~ 4 GB (al menos temporalmente)
Peter Lawrey
25

java.io.DataInput.readUTF()y java.io.DataOutput.writeUTF(String)digamos que un Stringobjeto está representado por dos bytes de información de longitud y la representación UTF-8 modificada de cada carácter en la cadena. Esto concluye que la longitud de la cadena está limitada por el número de bytes de la representación UTF-8 modificada de la cadena cuando se usa con DataInputy DataOutput.

Además, la especificaciónCONSTANT_Utf8_info encontrada en la especificación de máquina virtual Java define la estructura de la siguiente manera.

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

Puede encontrar que el tamaño de 'longitud' es de dos bytes .

Que el tipo de retorno de un determinado método (p String.length(). Ej. ) intNo siempre significa que su valor máximo permitido es Integer.MAX_VALUE. En cambio, en la mayoría de los casos, intse elige solo por razones de rendimiento. La especificación del lenguaje Java dice que los enteros cuyo tamaño es más pequeño que el de intse convierten intantes del cálculo (si mi memoria me sirve correctamente) y es una razón para elegir intcuando no hay una razón especial.

La longitud máxima en el momento de la compilación es como máximo 65536. Observe nuevamente que la longitud es el número de bytes de la representación UTF-8 modificada , no el número de caracteres en un Stringobjeto.

Stringlos objetos pueden tener muchos más personajes en tiempo de ejecución. Sin embargo, si desea utilizar Stringobjetos DataInpute DataOutputinterfaces, es mejor evitar el uso de Stringobjetos demasiado largos . Encontré esta limitación cuando implementé Objective-C equivalentes de DataInput.readUTF()y DataOutput.writeUTF(String).

Takahiko Kawasaki
fuente
1
Esta debería ser la respuesta predeterminada.
Nick
20

Como las matrices deben indexarse ​​con enteros, la longitud máxima de una matriz es Integer.MAX_INT(2 31 -1, o 2 147 483 647). Esto supone que tiene suficiente memoria para contener una matriz de ese tamaño, por supuesto.

Michael Myers
fuente
9

Tengo un iMac 2010 con 8GB de RAM, ejecutando Eclipse Neon.2 Release (4.6.2) con Java 1.8.0_25. Con el argumento VM -Xmx6g, ejecuté el siguiente 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());

Esto imprime:

Requested array size exceeds VM limit
1207959550

Entonces, parece que el tamaño máximo de la matriz es ~ 1,207,959,549. Entonces me di cuenta de que en realidad no nos importa si Java se queda sin memoria: solo estamos buscando el tamaño máximo de matriz (que parece ser una constante definida en alguna parte). Entonces:

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

Entonces, parece que el máximo es Integer.MAX_VALUE - 2, o (2 ^ 31) - 3

PD No estoy seguro de por qué mi StringBuildermáximo en 1207959550mientras que mi char[]máximo en (2 ^ 31) -3. Parece que AbstractStringBuilderduplica el tamaño de su interno char[]para hacerlo crecer, por lo que probablemente cause el problema.

Dantiston
fuente
1
Un tratamiento práctico muy útil de la pregunta
Pavlo Maistrenko
5

aparentemente está vinculado a un int, que es 0x7FFFFFFF (2147483647).

Francis
fuente
4

El tipo de retorno del método length () de la clase String es int .

public int length ()

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

Entonces el valor máximo de int es 2147483647 .

La cadena se considera como matriz de caracteres internamente, por lo que la indexación se realiza dentro del rango máximo. Esto significa que no podemos indexar el miembro 2147483648, por lo que la longitud máxima de String en Java es 2147483647.

El tipo de datos primitivo int es de 4 bytes (32 bits) en java. Como 1 bit (MSB) se usa como bit de signo , el rango está restringido dentro de -2 ^ 31 a 2 ^ 31-1 (-2147483648 a 2147483647). No podemos usar valores negativos para la indexación, por lo que obviamente el rango que podemos usar es de 0 a 2147483647.

Shanmugavel
fuente
0

Como se menciona en la respuesta de Takahiko Kawasaki , java representa cadenas Unicode en forma de UTF-8 modificado y en la estructura JSTM-Spec CONSTANT_UTF8_info , se asignan 2 bytes a la longitud (y no el número de caracteres de la cadena).
Para extender la respuesta, el método de la biblioteca de bytecode jvm de ASM contiene esto: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);
      }
    }
    ...
}

Pero cuando la asignación de puntos de código> 1byte, llama al 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;
      }
    }
   ...
}

En este sentido, la longitud máxima de la cadena es 65535 bytes, es decir, la longitud de codificación utf-8. y no charcontar
Puede encontrar el rango de puntos de código Unicode modificado de JVM, desde el enlace utf8 struct anterior.

DHS
fuente