Maksymalna długość łańcucha w Javie - wywołanie metody length ()


150

W Javie , jaka jest maksymalna wielkość Stringobiekt może posiadać, odnosząc się do length()wywołania metody?

Wiem, że length()zwracają rozmiar a Stringjako a char [];


5
Chociaż długość a Stringjest teoretycznie Integer.MAX_VALUE, długość literału ciągu w źródle wydaje się być ograniczona tylko do 65535 bajtów danych UTF-8.
200_success

Odpowiedzi:


169

Biorąc pod uwagę, że metoda Stringklasy lengthzwraca an int, maksymalna długość, jaka mogłaby zostać zwrócona przez metodę, wyniosłaby Integer.MAX_VALUE, czyli2^31 - 1 (lub około 2000000000).

Pod względem długości i indeksowania tablic (na przykład char[], co jest prawdopodobnie sposobem implementacji wewnętrznej reprezentacji danych dla Strings), Rozdział 10: Arrays of The Java Language Specification, Java SE 7 Edition mówi, co następuje:

Zmienne zawarte w tablicy nie mają nazw; zamiast tego odwołują się do nich wyrażenia dostępu do tablicy, które używają nieujemnych wartości indeksów całkowitych. Te zmienne nazywane są składnikami tablicy. Jeśli tablica zawiera nkomponenty, mówimy, że njest to długość tablicy; odwołania do składników tablicy są dokonywane za pomocą indeksów całkowitych od 0do n - 1włącznie.

Ponadto indeksowanie musi odbywać się według intwartości, jak wspomniano w sekcji 10.4 :

Tablice muszą być indeksowane według intwartości;

Dlatego wydaje się, że granica jest rzeczywiście 2^31 - 1, ponieważ jest to maksymalna wartość dla intwartości nieujemnej .

Jednak prawdopodobnie będą inne ograniczenia, takie jak maksymalny rozmiar, jaki można przydzielić dla tablicy.


26
W rzeczywistości Integer.MAX_VALUE to 2 ^ 31-1. :)
Michael Myers

1
Świetna odpowiedź człowieku! Rzuciłem okiem na kod źródłowy String.java i jest dobrze, „count” to zmienna int, która zwraca długość tablicy char, a tablica char jest przechowywana w zmiennej „value” (jako char []). że rozmiar ciągu może wynosić około 2 GB. Oczywiście mogą istnieć ograniczenia w przydzielaniu takiego rozmiaru pamięci. Dzięki!
taichi

5
Właśnie próbowałem zdefiniować literał ciągu w programie hello world java, który był dłuższy niż 65546. javacdaje błąd o tym, że ten literał jest za długi:javac HelloWorld.java 2>&1|head -c 80 HelloWorld.java:3: constant string too long
dlamblin

2
@dlamblin: To brzmi jak ograniczenie javacdla String literałów (nie Stringobiektów), ponieważ nie mogę znaleźć żadnego odniesienia do ograniczeń rozmiaru Stringliterałów w specyfikacji języka Java i specyfikacji JVM. Próbowałem utworzyć Stringliterał, który był większy niż 100 000 znaków, a kompilator Eclipse nie miał problemu z jego skompilowaniem. (A uruchomienie programu było w stanie wykazać, że literał miał String.lengthponad 100 000)
coobird,

3
@Premraj To było trzy lata temu, więc musiałem o tym pomyśleć. ;) Miałem na myśli; aby zbudować ciąg o maksymalnym rozmiarze, potrzebujesz dużo pamięci, być może i tak więcej niż masz. Potrzebujesz dwóch bajtów na znak ~ 4 GB, ale musisz to zbudować z StringBuildera lub char [], co oznacza, że ​​potrzebujesz kolejnych dwóch bajtów na znak, aby go utworzyć w pierwszej kolejności, tj. Kolejne ~ 4 GB (przynajmniej tymczasowo)
Peter Lawrey

25

java.io.DataInput.readUTF()i java.io.DataOutput.writeUTF(String)powiedzmy, że Stringobiekt jest reprezentowany przez dwa bajty informacji o długości i zmodyfikowaną reprezentację UTF-8 każdego znaku w ciągu. Wynika z tego, że długość String jest ograniczona przez liczbę bajtów zmodyfikowanej reprezentacji ciągu UTF-8, gdy jest używany z DataInputiDataOutput .

Ponadto specyfikacjaCONSTANT_Utf8_info znaleziona w specyfikacji wirtualnej maszyny języka Java definiuje strukturę w następujący sposób.

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

Możesz zauważyć, że rozmiar „length” to dwa bajty .

To, że typem zwracanym przez daną metodę (np. String.length()) Jest int, nie zawsze oznacza, że ​​dopuszczalna wartość maksymalna to Integer.MAX_VALUE. Zamiast tego w większości przypadków intjest wybierany tylko ze względu na wydajność. Specyfikacja języka Java mówi, że liczby całkowite, których rozmiar jest mniejszy niż rozmiar, intsą konwertowane intprzed obliczeniem (jeśli moja pamięć dobrze mi służy) i jest to jeden z powodów, dla których warto wybraćint gdy nie ma specjalnego powodu.

Maksymalna długość w czasie kompilacji wynosi maksymalnie 65536. Zwróć uwagę, że długość to liczba bajtów zmodyfikowanej reprezentacji UTF-8 , a nie liczba znaków w Stringobiekcie.

Stringobiekty mogą mieć znacznie więcej znaków w czasie wykonywania. Jeśli jednak chcesz używać Stringobiektów z interfejsami DataInputi DataOutput, lepiej unikać używania zbyt długich Stringobiektów. Znalazłem to ograniczenie, kiedy zaimplementowałem odpowiedniki Objective-C DataInput.readUTF()i DataOutput.writeUTF(String).


1
To powinna być domyślna odpowiedź.
Nick

20

Ponieważ matryce muszą być indeksowane liczbami, maksymalna długość tablicy jest Integer.MAX_INT(2 31 1, lub 2 147 483 647). Oczywiście przy założeniu, że masz wystarczająco dużo pamięci, aby pomieścić tablicę o takim rozmiarze.


9

Mam komputer iMac z 2010 r. Z 8 GB pamięci RAM, działający pod kontrolą Eclipse Neon.2 Release (4.6.2) z Javą 1.8.0_25. Z argumentem VM -Xmx6g uruchomiłem następujący kod:

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

To drukuje:

Requested array size exceeds VM limit
1207959550

Wydaje się więc, że maksymalny rozmiar tablicy to ~ 1,207,959,549. Wtedy zdałem sobie sprawę, że tak naprawdę nie obchodzi nas, czy Java zabraknie pamięci: po prostu szukamy maksymalnego rozmiaru tablicy (który wydaje się być gdzieś stałą zdefiniowaną). Więc:

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

Które wydruki:

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

Wydaje się więc, że maksymalna wartość to Integer.MAX_VALUE - 2 lub (2 ^ 31) - 3

PS Nie jestem pewien, dlaczego StringBuilderosiągnąłem maksimum na, podczas 1207959550gdy mój char[]maksimum przy (2 ^ 31) -3. Wydaje się, że AbstractStringBuilderpodwaja rozmiar jej wnętrzachar[] aby go rozwijać, więc prawdopodobnie powoduje problem.


1
Bardzo przydatne praktyczne podejście do pytania
Pavlo Maistrenko

5

najwyraźniej jest powiązany z liczbą int, która jest 0x7FFFFFFF (2147483647).


4

Typ Return metody length () klasy String to int .

public int length ()

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

Zatem maksymalna wartość int to 2147483647 .

Łańcuch jest wewnętrznie traktowany jako tablica znaków, więc indeksowanie odbywa się w maksymalnym zakresie. Oznacza to, że nie możemy zindeksować 2147483648. elementu członkowskiego, więc maksymalna długość ciągu znaków w java to 2147483647.

Prymitywny typ danych int to 4 bajty (32 bity) w java. Ponieważ 1 bit (MSB) jest używany jako bit znaku , zakres jest ograniczony od -2 ^ 31 do 2 ^ 31-1 (od -2147483648 do 2147483647). Nie możemy używać wartości ujemnych do indeksowania, więc oczywiście zakres, którego możemy użyć, wynosi od 0 do 2147483647.


0

Jak wspomniano w odpowiedzi Takahiko Kawasaki , java reprezentuje łańcuchy Unicode w postaci zmodyfikowanego UTF-8, aw JVM-Spec CONSTANT_UTF8_info Structure 2 bajty są przydzielane do długości (a nie liczby znaków w łańcuchu).
Aby rozszerzyć odpowiedź, metoda biblioteki kodu bajtowego ASM jvm zawiera to: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);
      }
    }
    ...
}

Ale gdy mapowanie punktu kodu> 1 bajt, wywołuje encodeUTF8metodę:

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

W tym sensie maksymalna długość łańcucha to 65535 bajtów, tj. Długość kodowania utf-8. and not charcount
Możesz znaleźć zmodyfikowany zakres punktów kodowych Unicode JVM, z powyższego linku utf8 struct.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.