¿Cuándo debemos usar el método interno de String en String literales?

187

De acuerdo con String # intern () , internse supone que el método devuelve el String del conjunto de String si el String se encuentra en el conjunto de String; de lo contrario, se agregará un nuevo objeto de string en el conjunto de String y se devolverá la referencia de este String.

Entonces intenté 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 samese imprimiera cuando s3 está internado y s1 and s2 are sameno se imprimirá. Pero el resultado es: ambas líneas se imprimen. Eso significa que, por defecto, las constantes de cadena están internadas. Pero si es así, ¿por qué necesitamos el internmétodo? En otras palabras, ¿cuándo debemos usar este método?

Rakesh Juyal
fuente
14
El Javadoc que vinculó también indica "Todas las cadenas literales y expresiones constantes con valores de cadena están internados".
Jorn
1
no es un duplicado exacto ..
Bozho
1
@Jorn: eso es correcto. Entonces, ¿por qué tenemos interncomo método público? ¿No deberíamos tener internun método privado para que nadie pueda tener acceso a él? ¿O hay algún propósito de este método?
Rakesh Juyal
2
@RakeshJuyal: el método interno se define en un tipo de cadena que puede ser literales de cadena o variables. ¿Cómo internarías una variable si el método fuera privado?
bobbyalex

Respuestas:

230

Java pasa automáticamente los literales de cadena. Esto significa que en muchos casos, el operador == parece funcionar para las cadenas de la misma manera que lo hace para ints u otros valores primitivos.

Dado que la internación es automática para los literales de cadena, el intern()método se debe usar 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 la s4variable, un valor para el cual se creó explícitamente utilizando el newoperador y donde el internmétodo no se usó en su resultado, es una instancia inmutable única que se devuelve el grupo constante de cadenas de JVM .

Consulte JavaTechniques "String Equality and Interning" para obtener más información.

Filipe Miguel Fonseca
fuente
Supongo que Java interna automáticamente los literales de cadena para fines de optimización. Puede hacer esto de forma segura solo porque las cadenas son inmutables, ¿correcto?
styfle
Nuevo en Java (soy del mundo C # .NET) y a veces veo en un proyecto heredado de Java "" .intern (), así que si lo entiendo correctamente, esto es "sin sentido" también para cadenas vacías.
hfrmobile
44
@Miguel Buena explicación, mi pregunta es cómo puede crearse un objeto aquí en su ejemplo. Aquí está mi suposición: String s1 = "Rakesh"; primer OB1 String s4 = new String("Rakesh");Segundo OB2 Entonces, el resto de (s2, s3, s5) hace referencia al mismo objeto (OB1) creado en el 'Conjunto de cadenas'. Entonces, ¿puedo decir que el .intern()método utilizado para evitar crear un nuevo objeto si la misma cadena está disponible en string poolIf mi suposición es incorrecta, así que dame dirección.
HybrisHelp
1
El enlace JavaTechniques está roto
SJuan76
20

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 cosas como los nombres de un conjunto modesto (quizás 100 o 200) corporaciones aparecieron por todas partes. Las estructuras de datos ya eran grandes, y si todos esos nombres de cuerpos 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 internas es que ==se puede usar (¡con éxito!) Para comparar cadenas si se garantiza que todas las cadenas involucradas serán internadas. Además de la sintaxis más ágil, esta también es una mejora de rendimiento. Pero como otros han señalado, hacer esto alberga un gran riesgo de introducir errores de programación, por lo que esto debe hacerse solo como una medida desesperada de último recurso.

La desventaja es que internar un String lleva más tiempo que simplemente lanzarlo al montón y que el espacio para los Strings internados puede ser limitado, dependiendo de la implementación de Java. Es mejor hacerlo cuando se trata de un número razonable conocido de cadenas con muchas duplicaciones.

Carl Smotricz
fuente
@ The downside is that interning a String takes more time than simply throwing it on the heap, and that the space for interned Strings may be limitedincluso si no utiliza el método interno para la constante de cadena, se internará automáticamente.
Rakesh Juyal
2
@Rakesh: generalmente no hay tantas constantes de cadena en una clase determinada, por lo que no es un problema de espacio / tiempo con constantes.
David Rodríguez - dribeas
Sí, el comentario de Rakesh no se aplica porque las cadenas internas solo se hacen (explícitamente) con cadenas que se "generan" de alguna manera, ya sea por manipulación interna o por recuperación de una base de datos o algo así. Con constantes no tenemos otra opción.
Carl Smotricz
2
+1. Creo que este es un buen ejemplo de cuando las prácticas tienen sentido. Sin ==embargo, no estoy de acuerdo con las cadenas.
Alexander Pogrebnyak
1
A partir de Java 7, el "conjunto de cadenas" se implementa en el espacio de almacenamiento dinámico, por lo que tiene todas las ventajas de almacenar pasantes, recolección de basura y el tamaño no está limitado, se puede aumentar hasta el tamaño de almacenamiento dinámico (nunca necesitará tanto). memoria para cadenas)
Anil Uttani
15

Quiero agregar mis 2 centavos al usar ==con cadenas internas.

Lo primero que String.equalshace es this==object.

Entonces, aunque hay una ganancia de rendimiento minúscula (no está llamando a un método), desde el punto de vista del mantenedor, el uso ==es una pesadilla, porque algunas cadenas internas tienen una tendencia a no ser internadas.

Por lo tanto, sugiero no confiar en un caso especial de ==cadenas internados, sino utilizar siempre equalscomo Gosling pretendía.

EDITAR: internado convirtiéndose en no interno:

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 hasReferenceValpúblico, sin entrar en muchos detalles, que espera una serie de cadenas internados.

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 usa una cadena no literal. Si equalsse usara en lugar de ==entonces hasReferenceVal, todavía habría seguido funcionando. Una vez más, el aumento de rendimiento es minúsculo, pero el costo de mantenimiento es alto.

Alexander Pogrebnyak
fuente
"Algunas cadenas internas tienen una tendencia a no ser internadas". wow, eso sería ... extraño. ¿Puedes citar una referencia, por favor?
Carl Smotricz
2
OK, pensé que te estabas refiriendo a que Strings en realidad salió del grupo de internos y se fue al montón gracias a la magia en la JVM. Lo que estás diciendo es que == hace que ciertas clases de errores de programador sean más probables.
Carl Smotricz
"Por lo tanto, sugiero no confiar en el caso especial de == para las cadenas internados, pero siempre usar iguales como Gosling pretendía". ¿Tiene una cita o comentario directo de Gosling que lo indique? Si ese es el caso, ¿por qué se molestó en poner intern () y el uso de == en el idioma?
1
Intern no es bueno para la comparación directa (==), aunque funciona si ambas cadenas están internados. es genial reducir la memoria total utilizada: cuando se usa la misma cadena en más de 1 lugar.
tgkprog
12

Los literales de cadena y las constantes se internan por defecto. Es decir, "foo" == "foo"(declarado por los literales de cadena), pero new String("foo") != new String("foo").

Bozho
fuente
44
Entonces, la pregunta es ¿cuándo debemos utilizar intern,
Rakesh Juyal
eso apuntaba a stackoverflow.com/questions/1833581/when-to-use-intern , y una serie de otras preguntas, algunas de ellas de ayer.
Bozho
Avíseme si mi comprensión de esta afirmación String literals and constants are interned by defaultes correcta. new String("foo")-> Aquí, se crea un Stro literal "foo" en el conjunto de String y uno en el montón, por lo que se crean un total de 2 objetos.
dkb
8

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 por defecto. Sin embargo, si deseamos ahorrar memoria, podríamos indicar que usemos la misma memoria mediante un concepto llamado string intern.

Las siguientes reglas lo ayudarán a comprender el concepto en términos claros:

  1. La clase de cadena mantiene un grupo interno que inicialmente está vacío. Este grupo debe garantizar que contenga objetos de cadena con solo valores únicos.
  2. Todos los literales de cadena que tienen el mismo valor deben considerarse el mismo objeto de ubicación de memoria porque de lo contrario no tienen noción de distinción. Por lo tanto, todos esos literales con el mismo valor harán una sola entrada en el conjunto interno y se referirán a la misma ubicación de memoria.
  3. La concatenación de dos o más literales también es un literal. (Por lo tanto, la regla # 2 será aplicable para ellos)
  4. Cada cadena creada como objeto (es decir, por cualquier otro método, excepto como literal) tendrá diferentes ubicaciones de memoria y no hará ninguna entrada en el grupo interno
  5. La concatenación de literales con no literales hará que no sea literal. Por lo tanto, el objeto resultante tendrá una nueva ubicación de memoria y NO hará una entrada en el grupo interno.
  6. Al invocar el método interno en un objeto de cadena, crea un nuevo objeto que ingresa al grupo interno o 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 motivacionales para el pasante interno no se discuten aquí. Sin embargo, el ahorro de memoria definitivamente será uno de los objetivos principales.

KGhatak
fuente
Gracias por el # 3, no lo sabía :)
kaay
4

debe distinguir el tiempo de dos períodos, que son el tiempo de compilación y el tiempo de ejecución, por ejemplo:

//example 1 
"test" == "test" // --> true 
"test" == "te" + "st" // --> true

//example 2 
"test" == "!test".substring(1) // --> false
"test" == "!test".substring(1).intern() // --> true

por un lado, en el ejemplo 1, encontramos que todos los resultados son verdaderos, porque en el tiempo de compilación, jvm pondrá la "prueba" en el conjunto de cadenas literales, si existe la "prueba" de jvm find, entonces utilizará el existente, en el ejemplo 1, las cadenas de "prueba" apuntan a la misma dirección de memoria, por lo que el ejemplo 1 devolverá verdadero. por otro lado, en el ejemplo 2, el método de substring () se ejecuta en el tiempo de ejecución, en el caso de "test" == "! test" .substring (1), el grupo creará dos objetos de cadena " test "y"! test ", por lo que son diferentes objetos de referencia, por lo que este caso devolverá falso, en el caso de" test "=="! test ".substring (1) .intern (), el método de interno ( ) colocará ""! test ".substring (1)" en el conjunto de cadenas literales,

thinkinjava
fuente
3

http://en.wikipedia.org/wiki/String_interning

El internamiento de cadenas es un método para almacenar solo una copia de cada valor de cadena distinto, que debe ser inmutable. Internar cadenas hace que algunas tareas de procesamiento de cadenas sean más eficientes en tiempo o espacio a costa de requerir más tiempo cuando la cadena se crea o se interna. Los valores distintos se almacenan en un grupo interno de cadenas.

bbcbcgb
fuente
2

Las cadenas internas evitan cadenas duplicadas. Interning ahorra RAM a expensas de más tiempo de CPU para detectar y reemplazar cadenas duplicadas. Solo hay una copia de cada Cadena que ha sido internada, sin importar cuántas referencias la señalen. Como las cadenas son inmutables, si dos métodos diferentes usan la misma cadena, pueden compartir una copia de la misma cadena. El proceso de convertir cadenas duplicadas en cadenas compartidas se llama interning.String.intern () le proporciona la dirección de la cadena maestra canónica. Puede comparar cadenas internas con simple == (que compara punteros) en lugar de igualesque compara los caracteres de la cadena uno por uno. Debido a que las cadenas son inmutables, el proceso interno es libre de ahorrar más espacio, por ejemplo, al no crear un literal de cadena separado para "pot" cuando existe como una subcadena de algún otro literal como "hipopótamo".

Para ver más http://mindprod.com/jgloss/interned.html

inter18099
fuente
2
String s1 = "Anish";
        String s2 = "Anish";

        String s3 = new String("Anish");

        /*
         * When the intern method is invoked, if the pool already contains a
         * string equal to this String object as determined by the
         * method, then the string from the pool is
         * returned. Otherwise, this String object is added to the
         * pool and a reference to this String object is returned.
         */
        String s4 = new String("Anish").intern();
        if (s1 == s2) {
            System.out.println("s1 and s2 are same");
        }

        if (s1 == s3) {
            System.out.println("s1 and s3 are same");
        }

        if (s1 == s4) {
            System.out.println("s1 and s4 are same");
        }

SALIDA

s1 and s2 are same
s1 and s4 are same
anish
fuente
2
String p1 = "example";
String p2 = "example";
String p3 = "example".intern();
String p4 = p2.intern();
String p5 = new String(p3);
String p6 = new String("example");
String p7 = p6.intern();

if (p1 == p2)
    System.out.println("p1 and p2 are the same");
if (p1 == p3)
    System.out.println("p1 and p3 are the same");
if (p1 == p4)
    System.out.println("p1 and p4 are the same");
if (p1 == p5)
    System.out.println("p1 and p5 are the same");
if (p1 == p6)
    System.out.println("p1 and p6 are the same");
if (p1 == p6.intern())
    System.out.println("p1 and p6 are the same when intern is used");
if (p1 == p7)
    System.out.println("p1 and p7 are the same");

Cuando se crean dos cadenas de forma independiente, le intern()permite compararlas y también le ayuda a crear una referencia en el grupo de cadenas si la referencia no existía antes.

Cuando lo usa String s = new String(hi), java crea una nueva instancia de la cadena, pero cuando lo usa String s = "hi", java verifica si hay una instancia de la palabra "hola" en el código o no, y si existe, simplemente devuelve la referencia.

Dado que la comparación de cadenas se basa en la referencia, le intern()ayuda a crear una referencia y le permite comparar el contenido de las cadenas.

Cuando lo usa intern()en el código, borra el espacio utilizado por la cadena que se refiere al mismo objeto y solo devuelve la referencia del mismo objeto ya existente en la memoria.

Pero en el caso de p5 cuando está usando:

String p5 = new String(p3);

Solo se copia el contenido de p3 y se crea p5 nuevamente. Entonces no está internado .

Entonces la salida será:

p1 and p2 are the same
p1 and p3 are the same
p1 and p4 are the same
p1 and p6 are the same when intern is used
p1 and p7 are the same
raja emani
fuente
2
    public static void main(String[] args) {
    // TODO Auto-generated method stub
    String s1 = "test";
    String s2 = new String("test");
    System.out.println(s1==s2);              //false
    System.out.println(s1==s2.intern());    //true --> because this time compiler is checking from string constant pool.
}
Anurag_BEHS
fuente
1

El método string intern () se usa para crear una copia exacta del objeto de cadena de montón en el grupo de cadenas constantes. Los objetos de cadena en el grupo de constantes de cadena se internan automáticamente, pero los objetos de cadena en el montón no. El uso principal de crear pasantes es ahorrar espacio en la memoria y realizar una comparación más rápida de los objetos de cadena.

Fuente: ¿Qué es string intern en java?

usuario2485429
fuente
1

Como dijiste, ese intern()método de cadena primero se encontrará en el grupo de cadenas, si lo encuentra, devolverá el objeto que apunta a eso o agregará una nueva cadena al grupo.

    String s1 = "Hello";
    String s2 = "Hello";
    String s3 = "Hello".intern();
    String s4 = new String("Hello");

    System.out.println(s1 == s2);//true
    System.out.println(s1 == s3);//true
    System.out.println(s1 == s4.intern());//true

El s1y s2son dos objetos que apuntan al conjunto de cadenas "Hola", y el uso "Hello".intern()encontrará que s1y s2. Entonces "s1 == s3"vuelve verdadero, así como a la s3.intern().

WendellWu
fuente
Esto realmente no proporciona mucha información nueva. Ya hay una respuesta exceptuada.
Alexander
0

Al usar la referencia de objeto de montón si queremos obtener la referencia de objeto de agrupación constante de cadena correspondiente , entonces deberíamos elegir intern ()

String s1 = new String("Rakesh");
String s2 = s1.intern();
String s3 = "Rakesh";

System.out.println(s1 == s2); // false
System.out.println(s2 == s3); // true

Vista pictórica ingrese la descripción de la imagen aquí

Paso 1: El objeto con datos 'Rakesh' se crea en el grupo constante de montón y cadena. También s1 siempre apunta al objeto de montón.

Paso 2: Al usar la referencia de objeto de montón s1, estamos tratando de obtener la referencia de objeto de agrupación constante de cadena correspondiente s2, usando intern ()

Paso 3: Crear intencionalmente un objeto con datos 'Rakesh' en el grupo constante de cadenas, referenciado por el nombre s3

Como operador "==" destinado a la comparación de referencia.

Obteniendo falso para s1 == s2

Obteniéndose verdadero para s2 == s3

¡¡Espero que esto ayude!!

Kms
fuente