Меня спросили об неизменных строках в Java. Мне было поручено написать функцию, которая объединяет несколько символов «a» в строку.
То, что я написал:
public String foo(int n) {
String s = "";
for (int i = 0; i < n; i++) {
s = s + "a"
}
return s;
}
Затем меня спросили, сколько строк сгенерирует эта программа, предполагая, что сборка мусора не происходит. Мои мысли для п = 3 было
- «»
- «А»
- «А»
- «Аа»
- «А»
- «Ааа»
- «А»
По сути, 2 строки создаются в каждой итерации цикла. Однако ответ был n 2 . Какие строки будут создаваться в памяти этой функцией и почему?
Ответы:
Строки 1 (
""
) и 2 ("a"
) являются константами в программе, они не создаются как часть вещей, а являются «интернированными», потому что они являются константами, о которых знает компилятор. Узнайте больше об этом в String interning в Википедии.Это также удаляет строки 5 и 7 из подсчета, так как они совпадают со
"a"
строкой # 2. Это оставляет строки № 3, № 4 и № 6. Ответ: «3 строки созданы для n = 3» с использованием вашего кода.Подсчет n 2 , очевидно, неверен, потому что при n = 3 это будет 9, и даже по вашему наихудшему ответу это будет только 7. Если ваши не интернированные строки были правильными, ответ должен был быть 2n + 1.
Итак, вопрос, как вы должны это сделать?
Поскольку строка является неизменяемой , вам нужна изменяемая вещь, которую вы можете изменить, не создавая новые объекты. Это StringBuilder .
Первое, на что нужно обратить внимание - это конструкторы. В этом случае мы знаем, какой длины будет строка, и есть конструктор,
StringBuilder(int capacity)
который означает, что мы выделяем ровно столько, сколько нам нужно.Далее,
"a"
не обязательно быть строкой , скорее это может быть персонаж'a'
. Это немного повышает производительность при вызовеappend(String)
vsappend(char)
- с помощьюappend(String)
метода необходимо выяснить, какова длина строки, и поработать над этим. С другой стороны,char
всегда ровно один символ в длину.Различия в коде можно увидеть в StringBuilder.append (String) против StringBuilder.append (char) . Это не то, о чем нужно слишком беспокоиться, но если вы пытаетесь произвести впечатление на работодателя, лучше всего использовать лучшие практики.
Итак, как это выглядит, когда вы сложите это вместе?
Один StringBuilder и одна строка были созданы. Никаких дополнительных строк не требуется для интернирования.
Напишите несколько других простых программ в Eclipse. Установите pmd и запустите его на код, который вы пишете. Обратите внимание, на что он жалуется, и исправьте эти вещи. Он нашел бы модификацию String с + в цикле, и если бы вы изменили его на StringBuilder, он, возможно, нашел бы начальную емкость, но он наверняка поймал бы разницу между
.append("a")
и.append('a')
источник
На каждой итерации, новый
String
создаются+
оператором и назначеноs
. После возвращения все они, кроме последнего, собираются в мусор.Строковые константы, как
""
и"a"
не создаются каждый раз, это интернированные строки . Поскольку строки являются неизменяемыми, они могут свободно использоваться совместно; это происходит со строковыми константами.Для эффективного объединения строк используйте
StringBuilder
.источник
Как объясняет MichaelT в своем ответе, ваш код выделяет O (n) строк. Но он также выделяет O (n 2 ) байтов памяти и выполняется за O (n 2 ) времени.
Он распределяет O (n 2 ) байтов, потому что строки, которые вы выделяете, имеют длины 0, 1, 2,…, n-1, n, которые суммируются в (n 2 + n) / 2 = O (n 2 ).
Время также равно O (n 2 ), поскольку выделение i-й строки требует копирования (i-1) -й строки, которая имеет длину i-1. Это означает, что каждый выделенный байт должен быть скопирован, что займет O (n 2 ) времени.
Может быть, это то, что имели в виду интервьюеры?
источник