Использование символа вместо 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, и это означает без необходимости размещать пространство в кучном пространстве.