java - ¿Cuántos objetos de cadena se crearían al concatenar cadenas múltiples?
string string-concatenation (7)
Me preguntaron en una entrevista sobre la cantidad de objetos que se crearán en el problema dado:
String str1 = "First";
String str2 = "Second";
String str3 = "Third";
String str4 = str1 + str2 + str3;
Respondí que habría 6 objetos creados en el grupo de cadenas.
3 sería para cada una de las tres variables.
1 sería parastr1 + str2
(digamosstr
).
1 sería parastr2 + str3
.
1 sería parastr + str3
(str = str1 + str2
).
¿Es correcta la respuesta que di? Si no, ¿cuál es la respuesta correcta?
Con la información dada, la pregunta no puede ser respondida definitivamente. Como se establece en el JLS, §15.18.1 :
... Para aumentar el rendimiento de la concatenación de cadenas repetida, un compilador de Java puede usar la clase
StringBuffer
o una técnica similar para reducir el número de objetos de cadena intermedios que se crean mediante la evaluación de una expresión.
Esto significa que la respuesta depende al menos del compilador concreto de Java utilizado.
Creo que lo mejor que podemos hacer es dar un intervalo como respuesta:
-
un compilador inteligente puede inferir que
str1
astr3
nunca se usan y plegar la concatenación durante la compilación, de modo que solo se crea unString
-object (el referenciado porstr4
) -
El número sensible máximo de
String
creadas debe ser 5: una parastr1
astr3
, una paratmp = str1 + str2
y otra parastr4 = tmp + str3
.
Entonces ... mi respuesta sería "algo entre uno y cinco objetos de
String
".
En cuanto a la cantidad total de objetos creados solo para esta operación ... No lo sé.
Esto también puede depender de cómo exactamente, por ejemplo, se implementa
StringBuffer
.
Como comentario: me pregunto cuál es la razón detrás de hacer tales preguntas. Normalmente, uno no necesita preocuparse por esos detalles.
Cualquier respuesta a su pregunta dependerá de la implementación de JVM y la versión de Java que se esté utilizando actualmente. Creo que es una pregunta irrazonable hacer en una entrevista.
Java 8
En mi máquina, con Java 1.8.0_201, su fragmento da como resultado este código de bytes
L0
LINENUMBER 13 L0
LDC "First"
ASTORE 1
L1
LINENUMBER 14 L1
LDC "Second"
ASTORE 2
L2
LINENUMBER 15 L2
LDC "Third"
ASTORE 3
L3
LINENUMBER 16 L3
NEW java/lang/StringBuilder
DUP
INVOKESPECIAL java/lang/StringBuilder.<init> ()V
ALOAD 1
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
ALOAD 2
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
ALOAD 3
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
ASTORE 4
lo que demuestra que se están creando
5 objetos
(3 literales de
String
*, 1
StringBuilder
, 1 instancia de
String
producida dinámicamente por
StringBuilder#toString
).
Java 12
En mi máquina, con Java 12.0.2, el código de bytes es
// identical to the bytecode above
L3
LINENUMBER 16 L3
ALOAD 1
ALOAD 2
ALOAD 3
INVOKEDYNAMIC makeConcatWithConstants(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; [
// handle kind 0x6 : INVOKESTATIC
java/lang/invoke/StringConcatFactory.makeConcatWithConstants(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
// arguments:
"/u0001/u0001/u0001"
]
ASTORE 4
que
magically
cambia "la respuesta correcta" a
4 objetos
ya que no hay ningún
StringBuilder
intermedio involucrado.
* Profundicemos un poco más.
12.5 Creación de nuevas instancias de clase
Una nueva instancia de clase puede crearse implícitamente en las siguientes situaciones:
- La carga de una clase o interfaz que contiene un literal de cadena ( §3.10.5 ) puede crear un nuevo objeto de cadena para representar el literal. (Esto no ocurrirá si una cadena que denota la misma secuencia de puntos de código Unicode ha sido internada previamente).
En otras palabras, cuando inicia una aplicación, ya hay objetos en el conjunto de cadenas. Apenas sabe qué son y de dónde provienen (a menos que escanee todas las clases cargadas para todos los literales que contienen).
La clase
java.lang.String
se cargará indudablemente como una clase JVM esencial, lo que significa que todos sus literales se crearán y se colocarán en el grupo.
Tomemos un fragmento seleccionado al azar del código fuente de
String
, seleccionemos un par de literales, coloquemos un punto de interrupción al comienzo de nuestro programa y examinemos si el grupo contiene estos literales.
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence,
Constable, ConstantDesc {
...
public String repeat(int count) {
// ...
if (Integer.MAX_VALUE / count < len) {
throw new OutOfMemoryError("Repeating " + len + " bytes String " + count +
" times will produce a String exceeding maximum size.");
}
}
...
}
Están allí de hecho.
Como hallazgo interesante, el filtrado de esta IDEA tiene un efecto secundario: las subcadenas que estaba buscando también se han agregado al grupo.
El tamaño del grupo aumentó en uno (se agregó
"bytes String"
) después de aplicar
this.contains("bytes String")
.
¿Dónde nos deja esto?
No tenemos idea de si
"First"
fue creado e internado antes de llamar a
String str1 = "First";
, por lo que no podemos afirmar con firmeza que la línea crea una nueva instancia.
Debería ser 5:
-
tres para los tres literales (asignados a
str1
,str3
ystr3
) -
uno para
str1 + str2
-
uno para
(result from the previous operation) + str3
(asignado astr4
)
Java 8 probablemente creará 5 objetos:
- 3 para los 3 literales
-
1
StringBuilder
-
1 para la
String
concatenada
Sin embargo, con Java 9 las
cosas cambiaron
y
String
concatenación de
String
ya no usa
StringBuilder
.
La operación de concatenación no crea esos muchos objetos de cadena.
Crea un
StringBuilder
y luego agrega las cadenas.
Entonces puede haber 5 objetos, 3 (variables) + 1 (sb) + 1 (cadena concatenada).
Se crearán 4 objetos de cadena en el grupo de cadenas constantes. 3 para literales y 1 con concatenación.
si usamos
String s1 = new String("one")
creará dos objetos, uno en grupo constante y otro en memoria de almacenamiento dinámico.
si definimos:
String s1 = "one";
String s2 = new String("one");
creará dos objetos, uno en grupo constante y otro en memoria de almacenamiento dinámico.
Una implementación de Java conforme puede concatenar las cadenas de varias maneras, en tiempo de ejecución o en tiempo de compilación, necesitando cualquier cantidad de objetos de tiempo de ejecución, incluidos los objetos cero si detecta que el resultado no es necesario en tiempo de ejecución.