Я только что нашел в своем проекте такую сборку sql-запроса:
return (new StringBuilder("select id1, " + " id2 " + " from " + " table")).toString();
Достигает ли это StringBuilder
своей цели, т.е. уменьшения использования памяти?
Я сомневаюсь в этом, потому что в конструкторе используется '+' (оператор конкатенации строк). Будет ли это занимать такой же объем памяти, как при использовании String, как в приведенном ниже коде? s Я понял, при использовании отличается StringBuilder.append()
.
return "select id1, " + " id2 " + " from " + " table";
Оба оператора одинаковы в использовании памяти или нет? Просьба уточнить.
Заранее спасибо!
Редактировать:
Кстати, это не мой код . Нашел в старом проекте. Кроме того, запрос не такой маленький, как в моем примере. :)
java
string
stringbuilder
Ваанду
источник
источник
PreparedStatement
или что-то подобное: docs.oracle.com/javase/tutorial/jdbc/basics/prepared.htmlОтветы:
Нет, совсем нет. Этот код используется
StringBuilder
неправильно. (Я думаю, вы неверно процитировали это; наверняка вокруг нет цитатid2
иtable
?)Обратите внимание, что цель (обычно) - уменьшить отток памяти, а не общий объем используемой памяти, чтобы немного облегчить жизнь сборщику мусора.
Нет, это вызовет больший отток памяти, чем просто указанное вами прямое соединение. (До тех пор, пока оптимизатор 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
вStringBuilder
конструкторе, так что он начинается с достаточной емкости для всего содержимого, которое мы собираемся добавить. Размер по умолчанию, используемый, если вы не укажете один, составляет 16 символов , что обычно слишком мало и приводит кStringBuilder
необходимости выполнять перераспределение, чтобы увеличить себя (IIRC, в Sun / Oracle JDK, он удваивается [или больше, если он знает, что ему нужно больше, чтобы удовлетворить конкретныйappend
] каждый раз, когда ему не хватает места).Возможно, вы слышали, что для конкатенации строк будет использоваться
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
используются несколько s: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;
... что довольно уродливо.
Однако важно помнить, что во всех случаях, за исключением очень немногих, это не имеет значения, и предпочтение отдается удобочитаемости (что повышает ремонтопригодность), за исключением конкретных проблем с производительностью.
источник
x + y + z
выражение,StringBuilder
если бы у меня не было веских оснований подозревать, что это будет серьезной проблемой.StringBuilder sql = new StringBuilder(" XXX); sql.append("nndmn");...
. Подобныеsql.append
строки составляют около 60 строк. Это нормально?StringBuilder
изначально будет выделено достаточно места для строки, которую вы передали конструктору, плюс 16 символов. Поэтому, если вы добавляете более 16 символов (я полагаю, что да, если есть 60 добавлений!), ТоStringBuilder
придется перераспределять по крайней мере один раз, а возможно и много раз. Если у вас есть разумное представление о том, насколько велик будет конечный результат (скажем, 400 символов), лучше всего сделатьsql = new StringBuilder(400);
(или что-то еще), а затем выполнитьappend
s.StringBuilder
то это заранее позволит сэкономить около восьми перераспределений памяти (если исходная строка была примерно 10 символов, SB было бы 26 для начала, затем удвоилось до 52, затем 104, 208, 416, 832, 1664, 3328 и, наконец, 6656). Только важно, если это точка доступа, но все же, если вы знаете заранее ... :-)Когда у вас уже есть все «кусочки», которые вы хотите добавить, нет никакого смысла в использовании
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();
В другой раз, когда я использую a,
StringBuilder
я строю строку из нескольких вызовов методов. Затем я могу создать методы, принимающиеStringBuilder
аргумент:private void addWhere(StringBuilder sb) { if (where != null) { sb.append(' ').append(where); } }
Когда вы используете
StringBuilder
, вы должны следить за любым использованием+
одновременно:sb.append("select " + fieldName);
Это
+
приведет к созданию другого внутреннегоStringBuilder
. Конечно, это должно быть:sb.append("select ").append(fieldName);
Наконец, как указывает @TJrowder, вы всегда должны угадывать размер файла
StringBuilder
. Это позволит сэкономить на количествеchar[]
создаваемых объектов при увеличении размера внутреннего буфера.источник
Вы правильно предполагаете, что цель использования конструктора строк не достигнута, по крайней мере, не в полной мере.
Однако, когда компилятор видит выражение,
"select id1, " + " id2 " + " from " + " table"
он генерирует код, который фактически создает скрытый объектStringBuilder
и добавляет к нему, поэтому конечный результат не так уж и плох.Но, конечно, любой, кто смотрит на этот код, обязательно думает, что он отсталый.
источник
В опубликованном вами коде не будет никаких преимуществ, поскольку вы неправильно используете StringBuilder. В обоих случаях создается одна и та же строка. Используя StringBuilder, вы можете избежать
+
операции со строками с помощьюappend
метода. Вы должны использовать это так:return new StringBuilder("select id1, ").append(" id2 ").append(" from ").append(" table").toString();
В Java тип String представляет собой неизменяемую последовательность символов, поэтому при добавлении двух строк виртуальная машина создает новое значение 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
источник
+
, который в любом случае будет преобразован в тот же код.StringBuilder
полезно, когда вы не можете выполнить всю конкатенацию в одном выражении, но не в этом случае.StringBuilder
если бы вы использовалиreturn "select id1, " + foo + "something else" + bar;
- так почему бы этого не сделать? Этот вопрос не указывает на то, что нужно что-то обходитьStringBuilder
стороной.Вы можете также использовать MessageFormat слишком
источник