Правильный способ использования StringBuilder в SQL
Я только что нашел в моем проекте сборку sql, подобную этой:
return (new StringBuilder("select id1, " + " id2 " + " from " + " table")).toString();
Достигает ли этот StringBuilder
свою цель, то есть уменьшает использование памяти?
Я сомневаюсь, что, поскольку в конструкторе используется «+» (оператор String concat). Будет ли такой объем памяти использоваться с использованием String, как в коде ниже? я понял, он отличается при использовании StringBuilder.append()
.
- Разница между строкой и StringBuilder в c #
- Является ли String.Format таким же эффективным, как StringBuilder
- Когда использовать StringBuilder?
- Как очистить или удалить StringBuilder?
- Как правильно использовать StringBuilder
return "select id1, " + " id2 " + " from " + " table";
Оба оператора равны в использовании памяти или нет? Просьба уточнить.
Заранее спасибо!
Редактировать:
Кстати, это не мой код . Нашел его в старом проекте. Кроме того, запрос не так мал, как в моем примере. 🙂
- интересное OutOfMemoryException с StringBuilder
- Удалить последний символ StringBuilder?
- В чем разница между String и StringBuffer в Java?
- Разница между StringBuilder и StringBuffer
- Емкость StringBuilder ()
- Почему StringBuilder, когда есть String?
- Как удалить пустые строки из форматированной строки?
- Java: String concat vs StringBuilder - оптимизирован, и что мне делать?
objective использования StringBuilder – сокращение памяти. Достигнуто ли это?
Нет, совсем нет. Этот код не использует StringBuilder
правильно. (Я думаю, что вы неверно процитировали его, но, конечно же, нет котировок вокруг id2
и table
?)
Обратите внимание, что целью (как правило) является сокращение памяти, а не общая память, чтобы сделать жизнь немного проще в сборщике мусора.
Будет ли это память равной использованию String, как показано ниже?
Нет, это приведет к большему сбою памяти, чем просто прямое цитирование, которое вы цитировали. (До тех пор, пока оптимизатор JVM не увидит, что явный StringBuilder
в коде не нужен и оптимизирует его, если это возможно.)
Если автор этого кода хочет использовать StringBuilder
(есть аргументы, но и против, см. Примечание в конце этого ответа), лучше сделать это правильно (здесь я предполагаю, что на самом деле нет котировок вокруг id2
и table
):
StringBuilder sb = new StringBuilder(some_appropriate_size); sb.append("select id1, "); sb.append(id2); sb.append(" from "); sb.append(table); return sb.toString();
Обратите внимание, что я перечислил some_appropriate_size
в конструкторе some_appropriate_size
, так что он начинается с достаточной емкости для полного содержимого, которое мы собираемся добавить. Размер по умолчанию, используемый, если вы не указываете один из них, составляет 16 символов , что обычно слишком мало и приводит к тому, что StringBuilder
должен выполнять перераспределение, чтобы сделать себя больше (IIRC, в Sun / Oracle JDK, он удваивает себя [или больше, если он знает, что ему нужно больше, чтобы удовлетворить конкретное приложение] каждый раз, когда он заканчивается из комнаты).
Возможно, вы слышали, что конкатенация строк будет использовать StringBuilder
под обложками, если скомпилирована с компилятором Sun / Oracle. Это верно, он будет использовать один StringBuilder
для общего выражения. Но он будет использовать конструктор по умолчанию, а это означает, что в большинстве случаев ему придется перераспределять ресурсы. Это легче читать. Обратите внимание, что это не относится к серии конкатенаций. Так, например, это использует один StringBuilder
:
return "prefix " + variable1 + " middle " + variable2 + " end";
Это примерно соответствует:
StringBuilder tmp = new StringBuilder(); // Using default 16 character size tmp.append("prefix "); tmp.append(variable1); tmp.append(" middle "); tmp.append(variable2); tmp.append(" end"); return tmp.toString();
Так что все в порядке, хотя конструктор по умолчанию и последующее перераспределение (ы) не являются идеальными, вероятность того, что он достаточно хорош, и конкатенация намного читаема.
Но это только для одного выражения. Для этого используются несколько StringBuilder
:
String s; s = "prefix "; s += variable1; s += " middle "; s += variable2; s += " end"; return s;
Это становится чем-то вроде этого:
String s; StringBuilder tmp; s = "prefix "; tmp = new StringBuilder(); tmp.append(s); tmp.append(variable1); s = tmp.toString(); tmp = new StringBuilder(); tmp.append(s); tmp.append(" middle "); s = tmp.toString(); tmp = new StringBuilder(); tmp.append(s); tmp.append(variable2); s = tmp.toString(); tmp = new StringBuilder(); tmp.append(s); tmp.append(" end"); s = tmp.toString(); return s;
… это довольно уродливо.
Однако важно помнить, что во всех случаях, кроме очень немногих случаев, это не имеет значения и требует читаемости (что улучшает ремонтопригодность) является предпочтительным, если не учитывать особую проблему производительности.
Когда у вас уже есть все «штуки», которые вы хотите добавить, нет смысла использовать StringBuilder
. Использование StringBuilder
и конкатенация строк в том же вызове, что и в вашем примере кода, еще хуже.
Это было бы лучше:
return "select id1, " + " id2 " + " from " + " table";
В этом случае конкатенация строк на самом деле происходит во время компиляции , поэтому она эквивалентна четному упрощению:
return "select id1, id2 from table";
Использование new StringBuilder().append("select id1, ").append(" id2 ")....toString()
фактически будет препятствовать производительности в этом случае, поскольку он заставляет конкатенацию выполняться во время выполнения , а не во время компиляции . К сожалению.
Если реальный код создает SQL-запрос, включая значения в запросе, то это еще одна отдельная проблема, которая заключается в том, что вы должны использовать параметризованные запросы, указав значения в параметрах, а не в SQL.
У меня есть статья о String
/ StringBuffer
которую я написал некоторое время назад – до StringBuilder
. Тем не менее, принципы применимы и к StringBuilder
.
[[Здесь есть несколько хороших ответов, но я обнаружил, что им все еще не хватает информации. ]]
return (new StringBuilder("select id1, " + " id2 " + " from " + " table")) .toString();
Итак, как вы указываете, пример, который вы даете, является упрощенным, но давайте проанализируем его в любом случае. Что здесь происходит, так это то, что компилятор действительно работает здесь, потому что "select id1, " + " id2 " + " from " + " table"
– это все константы. Таким образом, это превращается в:
return new StringBuilder("select id1, id2 from table").toString();
В этом случае, очевидно, нет смысла использовать StringBuilder
. Вы также можете:
// the compiler combines these constant strings return "select id1, " + " id2 " + " from " + " table";
Однако, даже если вы добавляли какие-либо поля или другие не константы, тогда компилятор использовал бы внутренний StringBuilder
– вам не нужно определять его:
// an internal StringBuilder is used here return "select id1, " + fieldName + " from " + tableName;
Под обложками это превращается в код, который приблизительно эквивалентен:
StringBuilder sb = new StringBuilder("select id1, "); sb.append(fieldName).append(" from ").append(tableName); return sb.toString();
Действительно, единственный раз, когда вам нужно напрямую использовать StringBuilder
вы имеете условный код. Например, код, который выглядит следующим образом, отчаялся для StringBuilder
:
// 1 StringBuilder used in this line String query = "select id1, " + fieldName + " from " + tableName; if (where != null) { // another StringBuilder used here query += ' ' + where; }
В первой строке + используется один экземпляр StringBuilder
. Затем +=
использует другой экземпляр StringBuilder
. Это более эффективно:
// choose a good starting size to lower chances of reallocation StringBuilder sb = new StringBuilder(64); sb.append("select id1, ").append(fieldName).append(" from ").append(tableName); // conditional code if (where != null) { sb.append(' ').append(where); } return sb.toString();
В другой раз, когда я использую StringBuilder
я создаю строку из нескольких вызовов методов. Затем я могу создать методы, которые принимают аргумент StringBuilder
:
private void addWhere(StringBuilder sb) { if (where != null) { sb.append(' ').append(where); } }
Когда вы используете StringBuilder
, вы должны следить за любым использованием +
в то же время:
sb.append("select " + fieldName);
Это +
приведет к StringBuilder
другого внутреннего StringBuilder
. Это, конечно же, должно быть:
sb.append("select ").append(fieldName);
Наконец, как указывает @TJrowder, вы всегда должны угадывать размер StringBuilder
. Это позволит сэкономить на количестве объектов char[]
созданных при увеличении размера внутреннего буфера.
Вы правы в предположении, что цель использования построителя строк не достигается, по крайней мере, не в полной мере.
Однако, когда компилятор видит выражение "select id1, " + " id2 " + " from " + " table"
он испускает код, который фактически создает StringBuilder
за кулисами и добавляет к нему, поэтому конечный результат не так уж плох после завершения ,
Но, конечно, любой, кто смотрит на этот код, должен думать, что он немного отсталый.
В коде, который вы опубликовали, не будет никаких преимуществ, поскольку вы злоупотребляете StringBuilder. В обоих случаях вы создаете ту же строку. Используя StringBuilder, вы можете избежать операции +
в Strings с помощью метода append
. Вы должны использовать его так:
return new StringBuilder("select id1, ").append(" id2 ").append(" from ").append(" table").toString();
В Java тип String является неотъемлемой последовательностью символов, поэтому, когда вы добавляете две строки, VM создает новое значение String с объединенными операндами.
StringBuilder предоставляет изменяемую последовательность символов, которую вы можете использовать для конкатенирования разных значений или переменных без создания новых объектов String, и поэтому иногда это может быть более эффективным, чем работа со строками
Это дает некоторые полезные функции, так как изменение содержимого последовательности символов, передаваемых как параметр внутри другого метода, что вы не можете сделать со строками.
private void addWhereClause(StringBuilder sql, String column, String value) { //WARNING: only as an example, never append directly a value to a SQL String, or you'll be exposed to SQL Injection sql.append(" where ").append(column).append(" = ").append(value); }
Дополнительная информация на http://docs.oracle.com/javase/tutorial/java/data/buffers.html
Вы также можете использовать MessageFormat