¿Cuándo deberíamos usar el método interno de String en literales de String?
Según String#intern() ,intern
se supone que el método devuelve la cadena del grupo de cadenas si la cadena se encuentra en el grupo de cadenas; de lo contrario, se agregará un nuevo objeto de cadena en el grupo de cadenas y se devolverá la referencia de esta cadena.
Entonces probé esto:
String s1 = "Rakesh";
String s2 = "Rakesh";
String s3 = "Rakesh".intern();
if ( s1 == s2 ){
System.out.println("s1 and s2 are same"); // 1.
}
if ( s1 == s3 ){
System.out.println("s1 and s3 are same" ); // 2.
}
Esperaba que s1 and s3 are same
se imprimiera cuando se internara s3, y s1 and s2 are same
no se imprimirá. Pero el resultado es: se imprimen ambas líneas. Eso significa que, de forma predeterminada, las constantes de cadena están internadas. Pero si es así, ¿por qué necesitamos el intern
método? En otras palabras, ¿cuándo deberíamos utilizar este método?
Java interna automáticamente literales de cadena. Esto significa que en muchos casos, el operador == parece funcionar para cadenas de la misma manera que lo hace para enteros u otros valores primitivos.
Dado que la pasantía es automática para los literales de cadena, el intern()
método debe usarse en cadenas construidas connew String()
Usando tu ejemplo:
String s1 = "Rakesh";
String s2 = "Rakesh";
String s3 = "Rakesh".intern();
String s4 = new String("Rakesh");
String s5 = new String("Rakesh").intern();
if ( s1 == s2 ){
System.out.println("s1 and s2 are same"); // 1.
}
if ( s1 == s3 ){
System.out.println("s1 and s3 are same" ); // 2.
}
if ( s1 == s4 ){
System.out.println("s1 and s4 are same" ); // 3.
}
if ( s1 == s5 ){
System.out.println("s1 and s5 are same" ); // 4.
}
volverá:
s1 and s2 are same
s1 and s3 are same
s1 and s5 are same
En todos los casos, además de s4
la variable, un valor para el cual se creó explícitamente usando new
el operador y donde intern
el método no se usó en su resultado, es una única instancia inmutable que se devuelve al grupo constante de cadenas de JVM .
Consulte JavaTechniques "Igualdad de cadenas e prácticas" para obtener más información.
En un proyecto reciente, se configuraron algunas estructuras de datos enormes con datos que se leyeron desde una base de datos (y por lo tanto no constantes/literales de cadena) pero con una gran cantidad de duplicación. Era una aplicación bancaria, y por todas partes aparecían cosas como los nombres de un conjunto modesto (tal vez 100 o 200) de corporaciones. Las estructuras de datos ya eran grandes, y si todos esos nombres de empresas hubieran sido objetos únicos, habrían desbordado la memoria. En cambio, todas las estructuras de datos tenían referencias a los mismos 100 o 200 objetos String, ahorrando así mucho espacio.
Otra pequeña ventaja de las cadenas internadas es que ==
se pueden usar (¡con éxito!) para comparar cadenas si se garantiza que todas las cadenas involucradas estarán internadas. Además de una sintaxis más sencilla, esto también supone una mejora del rendimiento. Pero como otros han señalado, hacer esto conlleva un gran riesgo de introducir errores de programación, por lo que debe hacerse sólo como medida desesperada y último recurso.
La desventaja es que internar un String lleva más tiempo que simplemente tirarlo al montón, y que el espacio para Strings internado puede ser limitado, dependiendo de la implementación de Java. Es mejor hacerlo cuando se trata de una cantidad razonable conocida de cadenas con muchas duplicaciones.
Prefiero String.equals
sobrethis==object
Quiero agregar mis 2 centavos al uso ==
con cadenas internas.
Lo primero String.equals
que hace es this==object
. Ver código fuente en OpenJDK .
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
…
Entonces, aunque hay una ganancia de rendimiento minúscula (no estás llamando a un método), desde el punto de vista del mantenedor, usarlo ==
es una pesadilla, porque algunas cadenas internas tienen una tendencia a no estar internadas.
Por lo tanto, sugiero no confiar en el caso especial de ==
cadenas internas, sino usarlo siempre equals
según lo previsto por Gosling.
EDITAR: internado convirtiéndose en no internado:
V1.0
public class MyClass
{
private String reference_val;
...
private boolean hasReferenceVal ( final String[] strings )
{
for ( String s : strings )
{
if ( s == reference_val )
{
return true;
}
}
return false;
}
private void makeCall ( )
{
final String[] interned_strings = { ... init with interned values ... };
if ( hasReference( interned_strings ) )
{
...
}
}
}
En la versión 2.0, el mantenedor decidió hacer hasReferenceVal
público, sin entrar en muchos detalles, que espera una serie de cadenas internas.
V2.0
public class MyClass
{
private String reference_val;
...
public boolean hasReferenceVal ( final String[] strings )
{
for ( String s : strings )
{
if ( s == reference_val )
{
return true;
}
}
return false;
}
private void makeCall ( )
{
final String[] interned_strings = { ... init with interned values ... };
if ( hasReference( interned_strings ) )
{
...
}
}
}
Ahora tiene un error que puede ser muy difícil de encontrar, porque en la mayoría de los casos la matriz contiene valores literales y, a veces, se utiliza una cadena no literal. Si equals
se hubieran utilizado en lugar de ==
entonces hasReferenceVal
, seguirían funcionando. Una vez más, la ganancia de rendimiento es minúscula, pero el costo de mantenimiento es alto.
Aprenda Java String Intern: de una vez por todas
Las cadenas en Java son objetos inmutables por diseño. Por lo tanto, dos objetos de cadena, incluso con el mismo valor, serán objetos diferentes de forma predeterminada. Sin embargo, si deseamos ahorrar memoria, podríamos indicar que se utilice la misma memoria mediante un concepto llamado string intern.
Las siguientes reglas le ayudarán a comprender el concepto en términos claros:
- La clase String mantiene un grupo interno que inicialmente está vacío. Este grupo debe garantizar que contenga objetos de cadena con valores únicos.
- Todos los literales de cadena que tienen el mismo valor deben considerarse objetos con la misma ubicación de memoria porque, de lo contrario, no tienen noción de distinción. Por lo tanto, todos los literales con el mismo valor realizarán una única entrada en el grupo interno y se referirán a la misma ubicación de memoria.
- La concatenación de dos o más literales también es un literal. (Por lo tanto, la regla número 2 será aplicable para ellos)
- Cada cadena creada como objeto (es decir, mediante cualquier otro método excepto literal) tendrá diferentes ubicaciones de memoria y no realizará ninguna entrada en el grupo interno.
- La concatenación de literales con no literales generará un no literal. Por lo tanto, el objeto resultante tendrá una nueva ubicación de memoria y NO realizará una entrada en el grupo interno.
- Al invocar el método interno en un objeto de cadena, se crea un nuevo objeto que ingresa al grupo interno o se devuelve un objeto existente del grupo que tiene el mismo valor. La invocación de cualquier objeto que no esté en el grupo interno NO mueve el objeto al grupo. Más bien crea otro objeto que ingresa al grupo.
Ejemplo:
String s1=new String ("abc");
String s2=new String ("abc");
If (s1==s2) //would return false by rule #4
If ("abc" == "a"+"bc" ) //would return true by rules #2 and #3
If ("abc" == s1 ) //would return false by rules #1,2 and #4
If ("abc" == s1.intern() ) //would return true by rules #1,2,4 and #6
If ( s1 == s2.intern() ) //wound return false by rules #1,4, and #6
Nota: Los casos de motivación para los pasantes de cadenas no se analizan aquí. Sin embargo, el ahorro de memoria será definitivamente uno de los objetivos principales.