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

150

В Java какой максимальный размер Stringможет иметь объект, ссылаясь на length()вызов метода?

Я знаю, что length()вернуть размер Stringкак char [];

таичи
источник
5
Хотя длина a Stringтеоретически Integer.MAX_VALUE, длина строкового литерала в источнике, по-видимому, ограничена только 65535 байтами данных UTF-8.
200_success

Ответы:

169

Учитывая, что метод Stringкласса lengthвозвращает значение 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значения.

Однако, вероятно, будут другие ограничения, такие как максимальный выделяемый размер для массива.

coobird
источник
26
Integer.MAX_VALUE на самом деле 2 ^ 31-1. :)
Майкл Майерс
1
Отличный ответ, человек! Я взглянул на исходный код String.java, и он прав: переменная «count» - это переменная int, которая возвращает длину массива char, а массив char сохраняется в переменной «value» (как char []). что размер строки может быть около 2 ГБ. Конечно, могут быть ограничения для выделения такого объема памяти. Спасибо!
Тайчи
5
Я только что попытался определить строковый литерал в java-программе hello world, которая была длиннее 65546. javacвыдает ошибку, что этот литерал слишком длинный:javac HelloWorld.java 2>&1|head -c 80 HelloWorld.java:3: constant string too long
dlamblin
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 представлением каждого символа в строке. Из этого следует, что длина строки ограничена количеством байтов модифицированного представления строки в UTF-8 при использовании с DataInputи DataOutput.

Кроме того, спецификацияCONSTANT_Utf8_info найденной в спецификации виртуальной машины Java определяет структуру следующим образом.

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

Вы можете найти, что размер '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

Таким образом, кажется, что максимум - Integer.MAX_VALUE - 2 или (2 ^ 31) - 3

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

dantiston
источник
1
Очень полезная практическая обработка вопроса
Павел Майстренко
5

по-видимому, он связан с int, который равен 0x7FFFFFFF (2147483647).

Фрэнсис
источник
4

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

public int length ()

См. Http://docs.oracle.com/javase/7/docs/api/java/lang/String.html#length ()

Таким образом, максимальное значение int составляет 2147483647 .

Внутренняя строка считается массивом символов, поэтому индексация выполняется в максимальном диапазоне. Это означает, что мы не можем индексировать 2147483648-й член. Таким образом, максимальная длина строки в java составляет 2147483647.

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

Shanmugavel
источник
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.

DHS
источник