Maximale Länge des Strings in Java - Aufruf der Methode length ()

150

Wie groß kann ein Objekt in Java maximal Stringsein, bezogen auf den length()Methodenaufruf?

Ich weiß, dass length()die Größe eines Stringals zurückgegeben wird char [];

Taichi
quelle
5
Während die Länge von a Stringtheoretisch ist Integer.MAX_VALUE, scheint die Länge eines String-Literal in der Quelle auf nur 65535 Byte UTF-8-Daten begrenzt zu sein.
200_erfolg

Antworten:

169

Unter Berücksichtigung der StringKlasse lengthMethode gibt ein int, die maximale Länge , die durch das Verfahren zurückgeführt werden würde , wäre Integer.MAX_VALUE, das ist 2^31 - 1(oder ungefähr 2 Milliarden.)

In Bezug auf Länge und Indizierung von Arrays (z. B. char[]wie die interne Datendarstellung wahrscheinlich für Strings implementiert wird ) wird in Kapitel 10: Arrays der Java-Sprachspezifikation, Java SE 7 Edition , Folgendes angegeben:

Die in einem Array enthaltenen Variablen haben keine Namen. Stattdessen werden sie durch Array-Zugriffsausdrücke referenziert, die nichtnegative ganzzahlige Indexwerte verwenden. Diese Variablen werden als Komponenten des Arrays bezeichnet. Wenn ein Array nKomponenten enthält, nist dies die Länge des Arrays. Auf die Komponenten des Arrays wird mit ganzzahligen Indizes von 0bis n - 1einschließlich verwiesen .

Darüber hinaus muss die Indizierung nach intWerten erfolgen, wie in Abschnitt 10.4 erwähnt :

Arrays müssen durch intWerte indiziert werden.

Daher scheint es, dass die Grenze tatsächlich ist 2^31 - 1, da dies der Maximalwert für einen nichtnegativen intWert ist.

Es wird jedoch wahrscheinlich andere Einschränkungen geben, z. B. die maximal zuweisbare Größe für ein Array.

Coobird
quelle
26
Integer.MAX_VALUE ist eigentlich 2 ^ 31-1. :)
Michael Myers
1
Großartige Antwort Mann! Ich habe mir den Quellcode von String.java angesehen und es ist richtig, 'count' ist die int-Variable, die die Länge des char-Arrays zurückgibt, und das char-Array wird in der 'value'-Variablen gespeichert (als char []) dass die String-Größe etwa 2 GB betragen könnte. Natürlich kann es Einschränkungen geben, eine solche Speichergröße zuzuweisen. Vielen Dank!
Taichi
5
Ich habe gerade versucht, ein String-Literal in einem Hallo-Welt-Java-Programm zu definieren, das länger als 65546 war. Es javacgibt einen Fehler, dass dieses Literal zu lang ist:javac HelloWorld.java 2>&1|head -c 80 HelloWorld.java:3: constant string too long
Dlamblin
2
@dlamblin: Das klingt nach einer Einschränkung javacfür String Literale (keine StringObjekte), da ich Stringin der Java-Sprachspezifikation und der JVM-Spezifikation keinen Verweis auf Größenbeschränkungen für Literale finden kann . Ich habe versucht, ein StringLiteral zu erstellen, das größer als 100.000 Zeichen war, und der Eclipse-Compiler hatte kein Problem beim Kompilieren. (Und das Ausführen des Programms konnte zeigen, dass das Literal String.lengthmehr als 100.000 hatte.)
Coobird
3
@Premraj Es war vor drei Jahren, also musste ich darüber nachdenken. ;) Was ich meinte, war; Um eine Zeichenfolge mit maximaler Größe zu erstellen, benötigen Sie viel Speicher, möglicherweise mehr als Sie ohnehin haben. Sie benötigen zwei Bytes pro Zeichen ~ 4 GB, aber Sie müssen dies aus einem StringBuilder oder char [] erstellen, was bedeutet, dass Sie zwei weitere Bytes pro Zeichen benötigen, um es überhaupt zu erstellen, dh weitere ~ 4 GB (zumindest vorübergehend)
Peter Lawrey
25

java.io.DataInput.readUTF()und java.io.DataOutput.writeUTF(String)sagen, dass ein StringObjekt durch zwei Bytes Längeninformation und die modifizierte UTF-8- Darstellung jedes Zeichens in der Zeichenfolge dargestellt wird. Dies schließt daraus, dass die Länge des Strings durch die Anzahl der Bytes der modifizierten UTF-8-Darstellung des Strings begrenzt ist, wenn er mit DataInputund verwendet wird DataOutput.

Darüber hinaus Die SpezifikationCONSTANT_Utf8_info definiert Gefunden in der Java Virtual Machine Spezifikation die Struktur wie folgt.

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

Sie können feststellen, dass die Größe von 'Länge' zwei Bytes beträgt .

Dass der Rückgabetyp einer bestimmten Methode (z. B. String.length()) ist, intbedeutet nicht immer, dass der zulässige Maximalwert ist Integer.MAX_VALUE. Stattdessen wird in den meisten Fällen intnur aus Leistungsgründen gewählt. Die Java-Sprachspezifikation besagt, dass Ganzzahlen, deren Größe kleiner als die von intist, intvor der Berechnung konvertiert werden (wenn mein Speicher mir richtig dient), und es ist ein Grund zu wählen, intwenn es keinen besonderen Grund gibt.

Die maximale Länge zur Kompilierungszeit beträgt höchstens 65536. Beachten Sie erneut, dass die Länge die Anzahl der Bytes der geänderten UTF-8- Darstellung und nicht die Anzahl der Zeichen in einem StringObjekt ist.

StringObjekte können zur Laufzeit möglicherweise viel mehr Zeichen enthalten. Wenn Sie jedoch StringObjekte mit DataInputund DataOutputSchnittstellen verwenden möchten , ist es besser, zu lange StringObjekte zu vermeiden . Ich habe diese Einschränkung festgestellt, als ich Objective-C-Äquivalente von DataInput.readUTF()und implementiert habe DataOutput.writeUTF(String).

Takahiko Kawasaki
quelle
1
Dies sollte die Standardantwort sein.
Nick
20

Da Arrays mit ganzen Zahlen indiziert werden müssen, beträgt die maximale Länge eines Arrays Integer.MAX_INT(2 31 -1 oder 2 147 483 647). Dies setzt natürlich voraus, dass Sie über genügend Speicher verfügen, um ein Array dieser Größe aufzunehmen.

Michael Myers
quelle
9

Ich habe einen 2010 iMac mit 8 GB RAM, auf dem Eclipse Neon.2 Release (4.6.2) mit Java 1.8.0_25 ausgeführt wird. Mit dem VM-Argument -Xmx6g habe ich den folgenden Code ausgeführt:

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

Dies druckt:

Requested array size exceeds VM limit
1207959550

Es scheint also, dass die maximale Arraygröße ~ 1.207.959.549 beträgt. Dann wurde mir klar, dass es uns eigentlich egal ist, ob Java nicht genügend Arbeitsspeicher hat: Wir suchen nur nach der maximalen Arraygröße (die eine irgendwo definierte Konstante zu sein scheint). So:

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

Welche Drucke:

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

Es scheint also, dass das Maximum Integer.MAX_VALUE - 2 oder (2 ^ 31) - 3 ist

PS Ich bin nicht sicher , warum mein StringBuilderausgereizt an , 1207959550während meine char[]ausgereizt an (2 ^ 31) -3. Es scheint, dass AbstractStringBuilderdie Größe seines internen verdoppelt char[], um es zu vergrößern, so dass wahrscheinlich das Problem verursacht.

Dantiston
quelle
1
Eine sehr nützliche praktische Behandlung der Frage
Pavlo Maistrenko
5

anscheinend ist es an ein int gebunden, das 0x7FFFFFFF (2147483647) ist.

Francis
quelle
4

Der Rückgabetyp der length () -Methode der String-Klasse ist int .

public int length ()

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

Der Maximalwert von int ist also 2147483647 .

Die Zeichenfolge wird intern als Zeichenarray betrachtet. Die Indizierung erfolgt also innerhalb des maximalen Bereichs. Dies bedeutet, dass wir das 2147483648-Mitglied nicht indizieren können. Die maximale Länge von String in Java beträgt also 2147483647.

Der primitive Datentyp int beträgt in Java 4 Byte (32 Bit). Da 1 Bit (MSB) als Vorzeichenbit verwendet wird , ist der Bereich auf -2 ^ 31 bis 2 ^ 31-1 (-2147483648 bis 2147483647) beschränkt. Wir können keine negativen Werte für die Indizierung verwenden. Der Bereich, den wir verwenden können, liegt offensichtlich zwischen 0 und 2147483647.

Shanmugavel
quelle
0

Wie in der Antwort von Takahiko Kawasaki erwähnt , repräsentiert Java Unicode-Strings in Form von modifiziertem UTF-8 und in der JVM-Spec CONSTANT_UTF8_info Structure werden der Länge 2 Bytes zugewiesen (und nicht der Anzahl der Zeichen von String).
Um die Antwort zu erweitern , enthält die Methode der ASM-JVM-Bytecode- Bibliothek Folgendes :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);
      }
    }
    ...
}

Wenn die Codepunktzuordnung> 1 Byte ist, wird die encodeUTF8Methode aufgerufen :

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

In diesem Sinne beträgt die maximale Zeichenfolgenlänge 65535 Byte, dh die utf-8-Codierungslänge. und nicht charzählen
Sie können den modifizierten Unicode-Codepunktbereich von JVM über den obigen utf8-Strukturlink finden.

DHS
quelle