Acabo de encontrar una construcción de consulta SQL como esta en mi proyecto:
return (new StringBuilder("select id1, " + " id2 " + " from " + " table")).toString();
¿Esto StringBuilder
logra su objetivo, es decir, reducir el uso de memoria?
Lo dudo, porque en el constructor se usa el '+' (operador String concat). ¿Tomará la misma cantidad de memoria que usar String como el siguiente código? s lo he entendido, difiere cuando se usa StringBuilder.append()
.
return "select id1, " + " id2 " + " from " + " table";
¿Ambas declaraciones son iguales en el uso de memoria o no? Por favor aclare.
¡Gracias por adelantado!
Editar:
Por cierto, no es mi código . Lo encontré en un proyecto antiguo. Además, la consulta no es tan pequeña como la de mi ejemplo. :)
java
string
stringbuilder
Vaandu
fuente
fuente
PreparedStatement
o algo similar: docs.oracle.com/javase/tutorial/jdbc/basics/prepared.htmlRespuestas:
No, en absoluto. Ese código no se está usando
StringBuilder
correctamente. (Sin embargo, creo que lo has citado mal; seguramente no hay citasid2
ytable
?)Tenga en cuenta que el objetivo (generalmente) es reducir la pérdida de memoria en lugar de la memoria total utilizada, para facilitar un poco la vida del recolector de basura.
No, causará más pérdida de memoria que el simple concat que citó. (Hasta / a menos que el optimizador de JVM vea que lo explícito
StringBuilder
en el código es innecesario y lo optimice, si puede).Si el autor de ese código quiere usar
StringBuilder
(hay argumentos a favor, pero también en contra; vea la nota al final de esta respuesta), es mejor hacerlo correctamente (aquí asumo que en realidad no hay citasid2
ytable
):StringBuilder sb = new StringBuilder(some_appropriate_size); sb.append("select id1, "); sb.append(id2); sb.append(" from "); sb.append(table); return sb.toString();
Tenga en cuenta que he enumerado
some_appropriate_size
en elStringBuilder
constructor, por lo que comienza con suficiente capacidad para el contenido completo que vamos a agregar. El tamaño predeterminado que se usa si no especifica uno es de 16 caracteres , que generalmente es demasiado pequeño y da como resultadoStringBuilder
tener que realizar reasignaciones para hacerse más grande (IIRC, en Sun / Oracle JDK, se duplica [o más, si sabe que necesita más para satisfacer una determinadaappend
] cada vez que se queda sin espacio).Usted puede haber oído que la concatenación de cadenas se utilice un
StringBuilder
bajo las sábanas si se compila con el compilador de Sun / Oracle. Esto es cierto, usará unoStringBuilder
para la expresión general. Pero usará el constructor predeterminado, lo que significa que en la mayoría de los casos tendrá que realizar una reasignación. Sin embargo, es más fácil de leer. Tenga en cuenta que esto no es cierto para una serie de concatenaciones. Entonces, por ejemplo, esto usa unoStringBuilder
:return "prefix " + variable1 + " middle " + variable2 + " end";
Se traduce aproximadamente a:
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();
Así que está bien, aunque el constructor predeterminado y las reasignaciones posteriores no son ideales, lo más probable es que sea lo suficientemente bueno, y la concatenación es mucho más legible.
Pero eso es solo para una sola expresión. Se
StringBuilder
utilizan varios s para esto:String s; s = "prefix "; s += variable1; s += " middle "; s += variable2; s += " end"; return s;
Eso termina convirtiéndose en algo como esto:
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;
... que es bastante feo.
Sin embargo, es importante recordar que en todos los casos , excepto en unos pocos , no importa y se prefiere ir con legibilidad (que mejora la capacidad de mantenimiento) salvo un problema de rendimiento específico.
fuente
x + y + z
expresión en lugar de aStringBuilder
menos que tuviera una buena razón para sospechar que sería un problema importante.StringBuilder sql = new StringBuilder(" XXX); sql.append("nndmn");...
.sql.append
Líneas similares son alrededor de 60 líneas. ¿Está bien esto?StringBuilder
inicialmente se le asignará suficiente espacio para la cadena que le pasó al constructor más 16 caracteres. Entonces, si agrega más de 16 caracteres (¡me atrevería a decir que sí, si hay 60 anexos!),StringBuilder
Tendrá que reasignar al menos una vez y posiblemente muchas veces. Si tiene una idea razonable de qué tan grande será el resultado final (digamos, 400 caracteres), es mejor hacersql = new StringBuilder(400);
(o lo que sea) y luego hacer loappend
s.StringBuilder
de antemano ahorrará alrededor de ocho reasignaciones de memoria (suponiendo que la cadena inicial fuera de unos 10 caracteres, el SB habría sido 26 para empezar, luego se duplicó a 52, luego 104, 208, 416, 832, 1664, 3328 y finalmente 6656). Solo es significativo si se trata de un punto de acceso, pero aún así, si lo sabe de antemano ... :-)Cuando ya tiene todas las "piezas" que desea agregar, no tiene sentido usarlas
StringBuilder
en absoluto. UtilizandoStringBuilder
y la concatenación de cadenas en la misma llamada según su código de ejemplo es aún peor.Esto sería mejor:
return "select id1, " + " id2 " + " from " + " table";
En este caso, la concatenación de cadenas está ocurriendo realmente en tiempo de compilación de todos modos, por lo que es equivalente a la incluso más simple:
return "select id1, id2 from table";
El uso en
new StringBuilder().append("select id1, ").append(" id2 ")....toString()
realidad obstaculizará el rendimiento en este caso, porque obliga a que la concatenación se realice en el momento de la ejecución , en lugar de en el momento de la compilación . ¡Ups!Si el código real está construyendo una consulta SQL al incluir valores en la consulta, entonces ese es otro problema separado , que es que debería usar consultas parametrizadas, especificando los valores en los parámetros en lugar de en SQL.
Tengo un artículo sobre el
String
/StringBuffer
la que escribí hace un tiempo - antes de queStringBuilder
se presentara. SinStringBuilder
embargo, los principios se aplican de la misma manera.fuente
[[Hay algunas buenas respuestas aquí, pero encuentro que todavía les falta un poco de información. ]]
return (new StringBuilder("select id1, " + " id2 " + " from " + " table")) .toString();
Entonces, como señala, el ejemplo que da es simplista, pero analicémoslo de todos modos. Lo que sucede aquí es que el compilador realmente hace el
+
trabajo aquí porque"select id1, " + " id2 " + " from " + " table"
son todas constantes. Entonces esto se convierte en:return new StringBuilder("select id1, id2 from table").toString();
En este caso, obviamente, no tiene sentido usar
StringBuilder
. También podrías hacer:// the compiler combines these constant strings return "select id1, " + " id2 " + " from " + " table";
Sin embargo, incluso si estuviera agregando campos u otras no constantes, el compilador usaría un interno
StringBuilder
; no es necesario que defina uno:// an internal StringBuilder is used here return "select id1, " + fieldName + " from " + tableName;
Bajo las sábanas, esto se convierte en un código que es aproximadamente equivalente a:
StringBuilder sb = new StringBuilder("select id1, "); sb.append(fieldName).append(" from ").append(tableName); return sb.toString();
Realmente, la única vez que necesita usar
StringBuilder
directamente es cuando tiene código condicional. Por ejemplo, el código que se parece a lo siguiente está desesperado por unStringBuilder
:// 1 StringBuilder used in this line String query = "select id1, " + fieldName + " from " + tableName; if (where != null) { // another StringBuilder used here query += ' ' + where; }
El
+
de la primera línea usa unaStringBuilder
instancia. Luego,+=
usa otraStringBuilder
instancia. Es más eficiente hacer:// 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();
Otra ocasión en la que utilizo un
StringBuilder
es cuando estoy construyendo una cadena a partir de varias llamadas a métodos. Entonces puedo crear métodos que tengan unStringBuilder
argumento:private void addWhere(StringBuilder sb) { if (where != null) { sb.append(' ').append(where); } }
Cuando esté usando a
StringBuilder
, debe estar atento a cualquier uso de+
al mismo tiempo:sb.append("select " + fieldName);
Eso
+
haráStringBuilder
que se cree otro interno . Por supuesto, esto debería ser:sb.append("select ").append(fieldName);
Por último, como señala @TJrowder, siempre debe adivinar el tamaño del
StringBuilder
. Esto ahorrará en la cantidad dechar[]
objetos creados al aumentar el tamaño del búfer interno.fuente
Tiene razón al suponer que el objetivo de usar el generador de cadenas no se logra, al menos no en toda su extensión.
Sin embargo, cuando el compilador ve la expresión
"select id1, " + " id2 " + " from " + " table"
, emite un código que en realidad crea unStringBuilder
detrás de escena y lo agrega, por lo que el resultado final no es tan malo después de todo.Pero, por supuesto, cualquiera que mire ese código seguramente pensará que es un poco retrasado.
fuente
En el código que ha publicado no habría ventajas, ya que está haciendo un mal uso de StringBuilder. Construyes la misma cadena en ambos casos. Usando StringBuilder puedes evitar la
+
operación en Strings usando elappend
método. Deberías usarlo de esta manera:return new StringBuilder("select id1, ").append(" id2 ").append(" from ").append(" table").toString();
En Java, el tipo de cadena es una secuencia inmutable de caracteres, por lo que cuando agrega dos cadenas, la máquina virtual crea un nuevo valor de cadena con ambos operandos concatenados.
StringBuilder proporciona una secuencia mutable de caracteres, que puede usar para concanear diferentes valores o variables sin crear nuevos objetos String, por lo que a veces puede ser más eficiente que trabajar con cadenas
Esto proporciona algunas características útiles, como cambiar el contenido de una secuencia de caracteres pasada como parámetro dentro de otro método, lo que no se puede hacer con Strings.
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); }
Más información en http://docs.oracle.com/javase/tutorial/java/data/buffers.html
fuente
+
, que se convertirá en el mismo código de todos modos.StringBuilder
es útil cuando no puede realizar toda la concatenación en una sola expresión, pero no en este caso.StringBuilder
si fuera a usarreturn "select id1, " + foo + "something else" + bar;
, entonces, ¿por qué no hacerlo? La pregunta no proporciona ninguna indicación de que algo deba pasarStringBuilder
.También puede usar MessageFormat también
fuente