Максимальна довжина рядка в Java - метод виклику length ()


150

У Java , який максимальний розмір Stringможе мати об'єкт, посилаючись на length()виклик методу?

Я знаю, що length()повертаємо розмір a Stringяк a char [];


5
Незважаючи на те, що довжина a Stringтеоретично Integer.MAX_VALUE, довжина рядкового літералу у джерелі обмежується лише 65535 байтом даних UTF-8.
200_успіх

Відповіді:


169

Зважаючи на те, що Stringклас ' lengthreturn return an int, максимальна довжина, яка повертається методом, буде такою Integer.MAX_VALUE, яка становить 2^31 - 1(або приблизно 2 млрд.)

З точки зору довжини і індексації масивів (наприклад char[], що, ймовірно , так як внутрішнє представлення даних здійснюється для Stringс), Глава 10: Масиви з специфікації мови Java, Java SE 7 Видання говорить наступне:

Змінні, що містяться в масиві, не мають імен; натомість на них посилаються вирази доступу до масиву, які використовують негативні цілочисельні значення індексу. Ці змінні називаються компонентами масиву. Якщо масив має nкомпоненти, ми говоримо, що nце довжина масиву; компоненти масиву використовують цілочисельні індекси від 0до n - 1включно.

Крім того, індексація повинна бути за intзначеннями, як зазначено у розділі 10.4 :

Масиви повинні бути індексовані intзначеннями;

Отже, виявляється, що межа дійсно є 2^31 - 1, оскільки це максимальне значення для негативного intзначення.

Однак, ймовірно, будуть інші обмеження, наприклад, максимальний розмір, який можна виділити для масиву.


26
Цілий.MAX_VALUE - це фактично 2 ^ 31-1. :)
Майкл Майєрс

1
Чудова відповідь людина! Я подивився на String.java вихідний код, і це правильно, 'count' - це змінна int, яка повертає довжину масиву char, а масив char зберігається у змінній 'value' (як char []) Це означає що розмір String може бути приблизно 2 Гб. Звичайно, можуть бути обмежені для розподілу такого розміру пам'яті. Дякую!
тайчі

5
Я щойно спробував визначити рядковий літерал у привітній програмі java world, яка була довшою 65546. javacдає помилку з приводу того, що цей javac HelloWorld.java 2>&1|head -c 80 HelloWorld.java:3: constant string too long
буквал

2
@dlamblin: Це звучить як обмеження javacдля String літералів (не Stringоб'єктів), оскільки я не можу знайти жодних посилань на обмеження розміру для Stringлітералів у специфікації мови Java та специфікації JVM. Я спробував зробити Stringлітерал, розмір якого перевищує 100 000 символів, і у компілятора Eclipse не виникло проблем із його складанням. (І запуск програми зміг показати, що літерал мав String.lengthбільше 100 000.)
coobird

3
@Premraj Це було три роки тому, тому мені довелося подумати над цим. ;) Що я мав на увазі, це було; щоб створити рядок максимального розміру, вам потрібно багато пам'яті, можливо більше, ніж у вас все одно. Вам потрібно два байти на символ ~ 4 Гб, але вам потрібно створити це з StringBuilder або char [], що означає, що вам потрібно ще два байти на символ, щоб створити його в першу чергу, тобто ще ~ 4 ГБ (принаймні тимчасово)
Пітер Лоурі

25

java.io.DataInput.readUTF()і java.io.DataOutput.writeUTF(String)кажуть, що Stringоб'єкт представлений двома байтами інформації про довжину та модифікованим представленням UTF-8 кожного символу в рядку. З цього випливає висновок, що довжина String обмежена кількістю байтів модифікованого представлення UTF-8 рядка при використанні з DataInputі DataOutput.

Крім того, специфікаціяCONSTANT_Utf8_info знайденої у специфікації віртуальної машини Java визначає структуру наступним чином.

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

Ви можете встановити, що розмір "довжини" - два байти .

Те, що тип повернення певного методу (наприклад String.length()) є intне завжди, означає, що його максимальне значення є Integer.MAX_VALUE. Натомість у більшості випадків intвибирається саме з міркувань продуктивності. Спеціалізація мови Java говорить, що цілі числа, розмір яких менший, ніж розмір int, перетворюються до intобчислення (якщо моя пам'ять служить мені правильно), і це одна причина, щоб вибрати, intколи немає особливої ​​причини.

Максимальна довжина під час компіляції - не більше 65536. Зауважте ще раз, що довжина - це кількість байтів модифікованого представлення UTF-8 , а не кількість символів в Stringоб'єкті.

Stringоб'єкти можуть мати значно більше символів під час виконання. Тим НЕ менше, якщо ви хочете використовувати Stringоб'єкти з DataInputі DataOutputінтерфейсів, то краще не використовувати занадто довгі Stringоб'єкти. Я знайшов це обмеження, коли реалізував еквіваленти Objective-C DataInput.readUTF()та DataOutput.writeUTF(String).


1
Це має бути відповідь за замовчуванням.
Нік

20

Оскільки масиви повинні бути проіндексовані цілими числами, максимальна довжина масиву становить Integer.MAX_INT(2 31 -1, або 2 147 483 647). Це, припускаючи, що у вас є достатньо пам'яті, щоб вмістити масив такого розміру, звичайно.


9

У мене є iMac 2010 року з 8 ГБ оперативної пам’яті, працює 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

Отже, здається, що макс є цілим. MAX_VALUE - 2, або (2 ^ 31) - 3

PS Я не впевнений, чому мій StringBuilderмаксимізований, 1207959550а мій - char[](2 ^ 31) -3. Здається, що AbstractStringBuilderвдвічі збільшується внутрішній розмір, char[]щоб виростити його, так що, ймовірно, викликає проблему.


1
Дуже корисне практичне трактування питання
Павло Майстренко


4

Тип повернення методу length () класу String - це int .

загальна довжина int ()

Зверніться http://docs.oracle.com/javase/7/docs/api/java/lang/String.html#length ()

Отже максимальне значення int становить 2147483647 .

Рядок вважається внутрішнім масивом char, тому індексація проводиться в межах максимального діапазону. Це означає, що ми не можемо індексувати 2147483648-го члена. Отже, максимальна довжина String в Java - 2147483647.

Примітивний тип даних int становить 4 байти (32 біта) в java. Оскільки 1 біт (MSB) використовується як бітовий знак , діапазон обмежений в межах від -2 ^ 31 до 2 ^ 31-1 (-2147483648 до 2147483647). Ми не можемо використовувати негативні значення для індексації. Тому, очевидно, діапазон, який ми можемо використовувати, становить від 0 до 2147483647.


0

Як згадується у відповіді Такахіко Кавасакі , java представляє рядки Unicode у вигляді модифікованого UTF-8 та в структурі JVM-Spec CONSTANT_UTF8_info , 2 байти виділяються по довжині (а не кількість символів рядка).
Щоб розширити відповідь, на ASM JVM байт - коді бібліотеки 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);
      }
    }
    ...
}

Але коли зіставлення кодової точки> 1байт, він викликає 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. і не charрахувати.
Ви можете знайти модифікований діапазон кодових точок коду Unicode JVM за вказаною вище посиланням utf8.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.