Использование символа вместо String для односимвольных значений в StringBuffer append
Я прошел через правило PMD AppendCharacterWithChar
. В нем сказано: избегайте конкатенации символов как строки в StringBuffer.append.
StringBuffer sb = new StringBuffer();
// Avoid this
sb.append("a");
// use instead something like this
StringBuffer sb = new StringBuffer();
sb.append('a');
Мне действительно нужно это правило PMD? Есть ли разница в производительности между двумя этими двумя частями кода?
String text = new StringBuffer().append("some string").append('c').toString();
String text = new StringBuffer().append("some string").append("c").toString();
Ответы
Ответ 1
Добавление символа в качестве char
всегда будет быстрее, чем добавление его как String
.
Но имеет ли разница в производительности? Если вы просто сделаете это один раз, это не так. Если внутри цикла повторяется его тело миллион раз, то да, это может иметь значение.
Если у вас уже есть символ во время компиляции, просто добавьте его как символ. Если он хранится в переменной со String
типом, не String.charAt(0)
его доступом, например, с помощью String.charAt(0)
или другими способами, просто добавьте String
.
На стороне Примечание:
StringBuilder
класс StringBuffer
для StringBuffer
. StringBuilder
быстрее, потому что его методы не синхронизированы (что вам не нужно в большинстве случаев).
На боковой ноте # 2:
Это не скомпилируется:
String text = new StringBuffer().append("some string").append('c');
append()
возвращает StringBuffer
для цепочки. Вам нужно вызвать toString()
:
String text = new StringBuffer().append("some string").append('c').toString();
Ответ 2
Из любопытства я провел микро-тест с jmh (включая мониторинг GC). Использование String незначительно медленнее, но разница минимальна: около 5 нс (наносекунды) за вызов и существенной разницы в активности GC.
Если вы append("c")
вместо append('c')
один миллион раз, это добавит 5 мс к вашей программе.
Результаты тестирования, включая время ого - n
представляет собой начальную длину StringBuilder:
Benchmark (n) Mode Cnt Score Error Units
SO28344.appendChar 0 avgt 30 16.476 ± 0.331 ns/op
SO28343294.appendChar:·gc.time 0 avgt 30 256.000 ms
SO28343294.appendString 0 avgt 30 22.048 ± 0.345 ns/op
SO28343294.appendString:·gc.time 0 avgt 30 220.000 ms
SO28343294.appendChar 50 avgt 30 17.323 ± 0.967 ns/op
SO28343294.appendChar:·gc.time 50 avgt 30 67.000 ms
SO28343294.appendString 50 avgt 30 20.944 ± 1.466 ns/op
SO28343294.appendString:·gc.time 50 avgt 30 74.000 ms
SO28343294.appendChar 1000 avgt 30 58.396 ± 0.811 ns/op
SO28343294.appendChar:·gc.time 1000 avgt 30 25.000 ms
SO28343294.appendString 1000 avgt 30 64.572 ± 4.779 ns/op
SO28343294.appendString:·gc.time 1000 avgt 30 24.000 ms
Код:
@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
public class SO28343294 {
@Param({"0", "50", "1000"}) int n;
Random r = new Random();
StringBuilder sb;
String s;
char c;
@Setup(Level.Invocation) public void populate() {
sb = new StringBuilder(n + 5);
for (int i = 0; i < n; i++) {
sb.append((char) (r.nextInt(26) + 'a'));
}
c = (char) (r.nextInt(26) + 'a');
s = new String(new char[] { c });
}
@Benchmark public StringBuilder appendString() {
return sb.append(s);
}
@Benchmark public StringBuilder appendChar() {
return sb.append(c);
}
}
Ответ 3
Посмотрите на реализацию каждого и сравните их:
public AbstractStringBuilder append(char c)
:
public AbstractStringBuilder append(char c) {
int newCount = count + 1;
if (newCount > value.length)
expandCapacity(newCount);
value[count++] = c;
return this;
}
public AbstractStringBuilder append(String str)
:
public AbstractStringBuilder append(String str) {
if (str == null) str = "null";
int len = str.length();
if (len == 0) return this;
int newCount = count + len;
if (newCount > value.length)
expandCapacity(newCount);
str.getChars(0, len, value, count);
count = newCount;
return this;
}
Какой из них вы предпочитаете, когда у вас есть возможность использовать оба?
Если у меня есть 1000 строк, я бы предпочел использовать append(char c)
для лучших характеристик, но для одной строки это не имеет большого значения.
Ответ 4
Да, его право Избегайте конкатенации символов как строк в StringBuffer.append, потому что всякий раз, когда вы пишете sb.append("a")
вы создаете объект String со значением a
а новый String означает новый объект String и новый объект String в Stringpool, и это означает без необходимости размещать пространство в кучном пространстве.