2010-09-27 3 views

Respuesta

114

Esto imprime true (aunque no usamos equals método: forma correcta de comparar cadenas)

String s = "a" + "bc"; 
    String t = "ab" + "c"; 
    System.out.println(s == t); 

Cuando compilador optimiza sus cadenas literales, se ve que tanto s y t tienen mismo valor y por lo tanto solo necesitas un objeto de cadena. Es seguro porque String es inmutable en Java.
Como resultado, tanto s como t apuntan al mismo objeto y se guarda algo de memoria.

El nombre 'grupo de cadenas' proviene de la idea de que todas las cadenas ya definidas se almacenan en algún 'grupo' y antes de crear un nuevo compilador de objetos String comprueba si dicha cadena ya está definida.

+2

Java tiene tipos de envoltura para tipos primitivos y esas clases son también inmutables. Como Integer, Charecter y Double .... etc. ¿También tienen un grupo para ahorrar memoria? Si no, ¿qué tiene de especial String para tener un grupo? –

+2

@PunithRaj ¡No estoy seguro! Sin embargo, lo dudo. int, por ejemplo, tiene solo 4 bytes, por lo que no se ahorrará mucho al tener dos puntos enteros en el mismo lugar de la memoria. Por el contrario, tener que mantener un 'grupo entero' para detectar valores repetitivos probablemente desperdicie más memoria de la que ahorrará al evitar los valores duplicados. –

+1

@PunithRaj String no es un tipo de datos primitivo (técnicamente/en cuanto a la implementación) y String no tiene una clase contenedora como cómo char/int do. – user3240361

37

No creo que realmente haga mucho, parece que es solo un caché para literales de cadenas. Si tiene varias cadenas cuyos valores son los mismos, todos señalarán el mismo literal de cadena en el grupo de cadenas.

String s1 = "Arul"; //case 1 
String s2 = "Arul"; //case 2 

En el caso 1, literal s1 se crea recientemente y se mantiene en el grupo. Pero en el caso 2, literal s2 refiere el s1, no creará uno nuevo en su lugar.

if(s1 == s2) System.out.println("equal"); //Prints equal. 

String n1 = new String("Arul"); 
String n2 = new String("Arul"); 
if(n1 == n2) System.out.println("equal"); //No output. 

http://p2p.wrox.com/java-espanol/29312-string-pooling.html

14

Cuando las clases de cargas de JVM, o de lo contrario ve una cadena literal, o alguna cadena sa código intern, se agrega la cadena a una tabla de búsqueda en su mayoría oculto que tiene una copia de cada uno de tales cuerda. Si se agrega otra copia, el tiempo de ejecución la organiza de modo que todos los literales se refieran al mismo objeto de cadena. Esto se llama "internamiento". Si usted dice algo así como

String s = "test"; 
return (s == "test"); 

se volverá true, porque la primera y la segunda "prueba" en realidad son el mismo objeto. La comparación de cadenas internas de esta manera puede ser mucho, mucho más rápido que String.equals, ya que hay una única comparación de referencia en lugar de una serie de comparaciones de char.

Puede agregar una cadena al grupo llamando al String.intern(), que le devolverá la versión agrupada de la cadena (que podría ser la misma cadena en la que está internándose, pero sería una locura confiar en eso - - a menudo no puede estar seguro exactamente qué código se ha cargado y ejecutar hasta ahora e internó la misma cadena). La versión agrupada (la cadena devuelta desde intern) será igual a cualquier literal idéntico. Por ejemplo:

String s1 = "test"; 
String s2 = new String("test"); // "new String" guarantees a different object 

System.out.println(s1 == s2); // should print "false" 

s2 = s2.intern(); 
System.out.println(s1 == s2); // should print "true" 
+0

De hecho, no creo que esté hecho en tiempo de ejecución. Incluso las cadenas más simples construidas con métodos no se agruparán. P.ej., el ejemplo de mi respuesta no funcionará si uso _concat_ en lugar de _ + _ –

+1

@Nikita: Eso es porque 'concat' no se puede optimizar tan fácilmente. Las cadenas combinadas con '+' probablemente serían pre-catted por cualquier compilador que se precie, porque el valor nunca cambia. Pero el compilador realmente no puede adivinar si una función devolverá el mismo valor todo el tiempo (algunas no), así que no lo intentará. Si utiliza 'concat' en su ejemplo," ab "," c "," a "y" bc "serían internados, pero" abc "no (porque no es literal, y su código no lo es) t 'interno'). Sin embargo, con '+' un compilador decente verá que ambas cadenas son "abc" y compilará eso. – cHao

+1

La internación * debe * hacerse en tiempo de ejecución, porque (1) la agrupación siempre comienza vacía, y (2) dos clases diferentes pueden tener cada una "abc" en ellas. Si el internamiento fuera una tarea de tiempo de compilación y ambas clases terminaron siendo cargadas, terminarían siendo dos "abc" s en el grupo de cadenas, lo que frustra el propósito completo del grupo de cadenas. – cHao

16

Vamos a empezar con una cita de la máquina virtual de especificación:

Carga de una clase o interfaz que contiene una cadena literal puede crear un nuevo objeto String (§2.4.8) para representar ese literal. Esto puede no ocurrir si el objeto String ya se ha creado para representar una ocurrencia previa de ese literal, o si el método String.intern se ha invocado en un objeto String que representa la misma cadena que el literal.

Esto no puede ocurrir - Este es un indicio, de que hay algo especial acerca de String objetos. Por lo general, al invocar un constructor se siempre crear una nueva instancia de la clase. Este no es el caso con Strings, especialmente cuando los objetos String se 'crean' con literales. Esas cadenas se almacenan en una tienda (pool) global, o al menos las referencias se mantienen en una agrupación, y cada vez que se necesita una nueva instancia de cadenas ya conocidas, la vm devuelve una referencia al objeto desde la agrupación. En pseudo código, puede ir así:

1: a := "one" 
    --> if(pool[hash("one")] == null) // true 
      pool[hash("one") --> "one"] 
     return pool[hash("one")] 

2: b := "one" 
    --> if(pool[hash("one")] == null) // false, "one" already in pool 
     pool[hash("one") --> "one"] 
     return pool[hash("one")] 

lo tanto, en este caso, las variables a y b referencias HOLD para la misma objeto. En este caso, tenemos (a == b) && (a.equals(b)) == true.

Este no es el caso si usamos el constructor:

1: a := "one" 
2: b := new String("one") 

Una vez más, "one" se crea en la piscina, pero entonces se crea una nueva instancia de la misma literal, y en este caso, lleva a (a == b) && (a.equals(b)) == false

Entonces ¿por qué tenemos un grupo de cadenas? Las cadenas y especialmente los literales de cadena son ampliamente utilizados en el código típico de Java. Y ellos son inmutables. Y al ser inmutable, se permite cachear cadenas para ahorrar memoria y aumentar el rendimiento (menos esfuerzo para la creación, menos basura recolectada).

Como programadores no tienen que cuidar mucho de la piscina de cuerda, siempre y cuando se tiene en cuenta:

  • (a == b) && (a.equals(b)) puede haber true o false (siempre uso equals para comparar cadenas)
  • no utilizar la reflexión para cambiar el respaldo char[] de una cadena (ya que no se sabe quién está usando actualling que String)
+0

Si * te importa * sobre el grupo de cadenas, existe la posibilidad de aumentos de rendimiento masivos en las aplicaciones que usan un pequeño grupo de cadenas de manera extensiva, generalmente como símbolos o palabras clave. Una vez que se intercalan las cadenas, la comparación se convierte en un solo '==' en lugar de la llamada a la función, dos llamadas a la longitud() y una posible agrupación de comparaciones que ocurriría con 'iguales'. – cHao

+0

@cHao Para mayor seguridad y consistencia, aún puede usar 'String.equals()' con cadenas internas, porque 'String.equals()' primero hace una comparación '==' – bcoughlan

+1

@bcoughlan: '==' es tan seguro y consistente como 'igual' - es simplemente incomprendido. Las personas que lo usan con objetos en general se dividen en dos categorías. Están aquellos que no entienden la semántica de valores versus identidad (y que == con tipos de referencia compara identidad) - esas personas * deberían * siempre usar 'String.equals'. Luego están aquellos que sí entienden, pero que conscientemente * eligen * la identidad. Y eso funciona igual de confiable, siempre y cuando sepa de dónde provienen sus objetos. Hay una razón por la cual '==' funciona con objetos, y en particular, por qué no solo llama a 'iguales'. – cHao