Quand devrions-nous utiliser la méthode interne de String sur les littéraux String

191

Selon String # intern () , la internméthode est censée renvoyer la chaîne à partir du pool de chaînes si la chaîne est trouvée dans le pool de chaînes, sinon un nouvel objet de chaîne sera ajouté dans le pool de chaînes et la référence de cette chaîne est renvoyée.

Alors j'ai essayé ceci:

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.
}

Je m'attendais à ce que ce s1 and s3 are samesoit imprimé car s3 est interné, et s1 and s2 are samene sera pas imprimé. Mais le résultat est: les deux lignes sont imprimées. Cela signifie que, par défaut, les constantes String sont internées. Mais si c'est le cas, pourquoi avons-nous besoin de la internméthode? En d'autres termes, quand devrions-nous utiliser cette méthode?

Rakesh Juyal
la source
15
Le Javadoc que vous avez lié indique également "Toutes les chaînes littérales et les expressions constantes à valeur de chaîne sont internées."
Jorn
1
pas une copie exacte ..
Bozho
1
@Jorn: c'est vrai. Alors pourquoi avons-nous interncomme méthode publique. Ne devrions-nous pas avoir interncomme méthode privée, pour que personne ne puisse y avoir accès. Ou y a-t-il un but de cette méthode?
Rakesh Juyal
2
@RakeshJuyal: La méthode interne est définie sur un type de chaîne qui peut être des chaînes littérales ou des variables. Comment interneriez-vous une variable si la méthode était privée?
bobbyalex

Réponses:

233

Java intègre automatiquement les littéraux String. Cela signifie que dans de nombreux cas, l'opérateur == semble fonctionner pour les chaînes de la même manière qu'il le fait pour les entiers ou d'autres valeurs primitives.

Puisque l'internalisation est automatique pour les littéraux de chaîne, la intern()méthode doit être utilisée sur les chaînes construites avecnew String()

En utilisant votre exemple:

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.
}

retournera:

s1 and s2 are same
s1 and s3 are same
s1 and s5 are same

Dans tous les cas en plus de s4variable, une valeur pour laquelle a été explicitement créée à l'aide de l' newopérateur et où la internméthode n'a pas été utilisée sur son résultat, c'est une seule instance immuable qui est retournée dans le pool de constantes de chaîne de la JVM .

Reportez-vous à JavaTechniques «Égalité des chaînes et internement» pour plus d'informations.

Filipe Miguel Fonseca
la source
Je suppose que Java utilise automatiquement les littéraux String à des fins d'optimisation. Il peut le faire en toute sécurité uniquement parce que les chaînes sont immuables, n'est-ce pas?
styfle
Nouveau sur Java (je suis du monde C # .NET) et je vois parfois dans un projet hérité Java "" .intern () donc si je comprends bien que c'est "absurde" aussi pour les chaînes vides.
hfrmobile
4
@Miguel Belle explication, ma question est de savoir comment l'objet peut être créé ici dans votre exemple. Voici mon hypothèse: String s1 = "Rakesh"; premier OB1 String s4 = new String("Rakesh");Deuxième OB2 Donc, le reste de (s2, s3, s5) fait référence au même objet (OB1) créé dans `` string Pool '' Alors puis-je dire que la .intern()méthode utilisée pour empêcher de créer un nouvel objet si la même chaîne est disponible dans string poolIf mon hypothèse est fausse alors donnez-moi une direction.
HybrisHelp
1
Le lien JavaTechniques est rompu
SJuan76
20

Sur un projet récent, d'énormes structures de données ont été configurées avec des données lues à partir d'une base de données (et donc pas de constantes / littéraux de chaîne) mais avec une énorme quantité de duplication. C'était une application bancaire, et des choses comme les noms d'un ensemble modeste (peut-être 100 ou 200) sociétés apparaissaient partout. Les structures de données étaient déjà volumineuses, et si tous ces noms de corp avaient été des objets uniques, ils auraient débordé de mémoire. Au lieu de cela, toutes les structures de données avaient des références aux mêmes 100 ou 200 objets String, économisant ainsi beaucoup d'espace.

Un autre petit avantage des chaînes internes est qu'elles ==peuvent être utilisées (avec succès!) Pour comparer des chaînes si toutes les chaînes impliquées sont garanties d'être internées. Outre la syntaxe allégée, il s'agit également d'une amélioration des performances. Mais comme d’autres l’ont souligné, cela comporte un grand risque d’introduire des erreurs de programmation, et cela ne devrait donc être fait qu’en dernier recours.

L'inconvénient est que l'internement d'une chaîne prend plus de temps que de simplement la jeter sur le tas, et que l'espace pour les chaînes internes peut être limité, en fonction de l'implémentation Java. Il vaut mieux le faire lorsque vous avez affaire à un nombre raisonnable connu de chaînes avec de nombreuses duplications.

Carl Smotricz
la source
@ 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 limitedmême si vous n'utilisez pas la méthode interne pour la constante String, elle sera automatiquement internée.
Rakesh Juyal
2
@Rakesh: Il n'y a généralement pas beaucoup de constantes String dans une classe donnée, donc ce n'est pas un problème d'espace / temps avec des constantes.
David Rodríguez - dribeas
Oui, le commentaire de Rakesh ne s'applique pas car l'internement de chaînes est uniquement (explicitement) fait avec des chaînes qui sont "générées" d'une manière ou d'une autre, que ce soit par manipulation interne ou par récupération à partir d'une base de données ou autre. Avec les constantes, nous n'avons pas le choix.
Carl Smotricz
2
+1. Je pense que c'est un bon exemple de quand un stage a du sens. Je ne suis pas d'accord sur les ==cordes cependant.
Alexander Pogrebnyak
1
À partir de Java 7, le «pool de chaînes» est implémenté dans l'espace du tas, il obtient donc tous les avantages du stockage des stagiaires, du ramasse-miettes et la taille de celui-ci n'est pas limitée, il peut être augmenté jusqu'à la taille du tas (vous n'en aurez jamais besoin autant mémoire pour les chaînes)
Anil Uttani
15

Je veux ajouter mes 2 cents sur l'utilisation ==avec des chaînes internes.

La première chose à String.equalsfaire est this==object.

Donc, bien qu'il y ait un gain de performance minuscule (vous n'appelez pas de méthode), du point de vue du mainteneur, l'utilisation ==est un cauchemar, car certaines chaînes internées ont tendance à devenir non internées.

Je suggère donc de ne pas compter sur le cas particulier des ==chaînes internes, mais de toujours les utiliser equalscomme Gosling l'a prévu.

EDIT: internés devenant non internés:

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 ) )
     {
        ...
     }
  }
}

Dans la version 2.0, le mainteneur a décidé de rendre hasReferenceValpublic, sans entrer dans les détails, qu'il attend un tableau de chaînes internes.

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 ) )
     {
        ...
     }
  }
}

Vous avez maintenant un bogue, qui peut être très difficile à trouver, car dans la majorité des cas, le tableau contient des valeurs littérales et parfois une chaîne non littérale est utilisée. Si equalson a utilisé au lieu de ==puis hasReferenceValaurait encore continuer à travailler. Encore une fois, le gain de performances est minime, mais le coût de maintenance est élevé.

Alexander Pogrebnyak
la source
"certaines chaînes internées ont tendance à devenir non internées." wow, ce serait ... étrange. Pouvez-vous citer une référence, s'il vous plaît?
Carl Smotricz
2
OK, je pensais que vous faisiez référence à Strings errant hors du pool interne et sur le tas grâce à la magie dans la JVM. Ce que vous dites, c'est que == rend certaines classes d'erreurs de programmeur plus probables.
Carl Smotricz
"Je suggère donc de ne pas compter sur le cas particulier de == pour les chaînes internées, mais d'utiliser toujours égal comme Gosling l'a prévu." Avez-vous une citation ou un commentaire direct de Gosling à ce sujet? Si c'est le cas, pourquoi a-t-il même pris la peine de mettre intern () et l'utilisation de == dans le langage?
1
intern n'est pas bon pour la comparaison directe (==), même si cela fonctionne si les deux chaînes sont internées. il est bon de réduire la mémoire totale utilisée: lorsque la même chaîne est utilisée à plus d'un endroit.
tgkprog
12

Les littéraux de chaîne et les constantes sont internés par défaut. Autrement dit, "foo" == "foo"(déclaré par les littéraux String), mais new String("foo") != new String("foo").

Bozho
la source
4
Donc, la question est de savoir quand devrions-nous utiliser intern,
Rakesh Juyal
cela a été signalé à stackoverflow.com/questions/1833581/when-to-use-intern , et à un certain nombre d'autres questions, dont certaines d'hier.
Bozho
Faites-moi savoir si ma compréhension de cette déclaration String literals and constants are interned by default:, est correcte. new String("foo")-> Ici, un littéral String "foo" est créé dans le pool String et un dans le tas, donc 2 objets au total sont créés.
dkb
9

Apprendre Java String Intern - une fois pour toutes

Les chaînes en java sont des objets immuables de par leur conception. Par conséquent, deux objets chaîne, même avec la même valeur, seront des objets différents par défaut. Cependant, si nous souhaitons économiser de la mémoire, nous pourrions indiquer d'utiliser la même mémoire par un concept appelé string intern.

Les règles ci-dessous vous aideront à comprendre le concept en termes clairs:

  1. La classe String gère un pool interne qui est initialement vide. Ce pool doit garantir de contenir des objets chaîne avec uniquement des valeurs uniques.
  2. Tous les littéraux de chaîne ayant la même valeur doivent être considérés comme le même objet d'emplacement mémoire car ils n'ont par ailleurs aucune notion de distinction. Par conséquent, tous ces littéraux avec la même valeur feront une seule entrée dans le pool interne et feront référence au même emplacement mémoire.
  3. La concaténation de deux ou plusieurs littéraux est également un littéral. (Par conséquent, la règle n ° 2 sera applicable pour eux)
  4. Chaque chaîne créée en tant qu'objet (c'est-à-dire par toute autre méthode sauf en tant que littéral) aura des emplacements mémoire différents et ne fera aucune entrée dans le pool interne
  5. La concaténation de littéraux avec des non-littéraux rendra un non-littéral. Ainsi, l'objet résultant aura un nouvel emplacement mémoire et ne fera PAS une entrée dans le pool interne.
  6. L'appel de la méthode interne sur un objet chaîne crée un nouvel objet qui entre dans le pool interne ou renvoie un objet existant du pool qui a la même valeur. L'appel sur tout objet qui n'est pas dans le pool interne, ne déplace PAS l'objet vers le pool. Cela crée plutôt un autre objet qui entre dans la piscine.

Exemple:

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

Remarque: Les cas de motivation pour string intern ne sont pas abordés ici. Cependant, la sauvegarde de la mémoire sera certainement l'un des principaux objectifs.

KGhatak
la source
1
Merci pour le # 3, je ne savais pas :)
kaay
4

vous devriez faire deux périodes de temps qui sont le temps de compilation et le temps d'exécution, par exemple:

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

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

d'une part, dans l'exemple 1, nous trouvons que les résultats sont tous retournés true, car au moment de la compilation, le jvm mettra le "test" dans le pool de chaînes littérales, si le jvm find "test" existe, alors il utilisera celui qui existe, dans l'exemple 1, les chaînes "test" pointent toutes vers la même adresse mémoire, donc l'exemple 1 retournera vrai. en revanche, dans l'exemple 2, la méthode de substring () s'exécute au moment de l'exécution, dans le cas de "test" == "! test" .substring (1), le pool créera deux objets string, " test "et"! test ", donc ce sont des objets de référence différents, donc ce cas retournera faux, dans le cas de" test "=="! test ".substring (1) .intern (), la méthode de intern ( ) placera le ""! test ".substring (1)" dans le pool de chaînes littérales,

thinkinjava
la source
3

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

l'internation de chaînes est une méthode de stockage d'une seule copie de chaque valeur de chaîne distincte, qui doit être immuable. L'intégration de chaînes rend certaines tâches de traitement de chaînes plus efficaces en termes de temps ou d'espace, au prix de nécessiter plus de temps lorsque la chaîne est créée ou internée. Les valeurs distinctes sont stockées dans un pool interne de chaînes.

bbcbcgb
la source
2

Les chaînes internes évitent les chaînes en double. L'intégration économise de la RAM au détriment de plus de temps CPU pour détecter et remplacer les chaînes en double. Il n'y a qu'une seule copie de chaque chaîne qui a été internée, quel que soit le nombre de références qui la pointent. Étant donné que les chaînes sont immuables, si deux méthodes différentes utilisent accidentellement la même chaîne, elles peuvent partager une copie de la même chaîne. Le processus de conversion des chaînes dupliquées en chaînes partagées s'appelle interning.String.intern () vous donne l'adresse de la chaîne maître canonique. Vous pouvez comparer des chaînes internes avec un simple == (qui compare des pointeurs) au lieu d' égauxqui compare les caractères de la chaîne un par un. Étant donné que les chaînes sont immuables, le processus interne est libre d'économiser davantage d'espace, par exemple, en ne créant pas de littéral String distinct pour "pot" lorsqu'il existe en tant que sous-chaîne d'un autre littéral tel que "hippopotame".

Pour en savoir plus http://mindprod.com/jgloss/interned.html

inter18099
la source
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");
        }

PRODUCTION

s1 and s2 are same
s1 and s4 are same
anish
la source
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");

Lorsque deux chaînes sont créées indépendamment, intern()vous permet de les comparer et vous aide également à créer une référence dans le pool de chaînes si la référence n'existait pas auparavant.

Lorsque vous utilisez String s = new String(hi), java crée une nouvelle instance de la chaîne, mais lorsque vous utilisez String s = "hi", java vérifie s'il y a une instance du mot "hi" dans le code ou non et si elle existe, il renvoie simplement la référence.

Étant donné que la comparaison de chaînes est basée sur une référence, elle vous intern()aide à créer une référence et vous permet de comparer le contenu des chaînes.

Lorsque vous utilisez intern()dans le code, il efface l'espace utilisé par la chaîne faisant référence au même objet et renvoie simplement la référence du même objet déjà existant en mémoire.

Mais dans le cas de p5 lorsque vous utilisez:

String p5 = new String(p3);

Seul le contenu de p3 est copié et p5 est créé à nouveau. Il n'est donc pas interné .

Ainsi, la sortie sera:

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
la source
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
la source
1

La méthode string intern () est utilisée pour créer une copie exacte de l'objet chaîne de tas dans le pool de constantes de chaîne. Les objets chaîne du pool de constantes chaîne sont automatiquement internés, mais les objets chaîne du tas ne le sont pas. L'utilisation principale de la création de stagiaires est d'économiser de l'espace mémoire et d'effectuer une comparaison plus rapide des objets chaîne.

Source: Qu'est-ce que string intern en java?

user2485429
la source
1

Comme vous l'avez dit, cette intern()méthode de chaîne trouvera d'abord à partir du pool de chaînes, si elle le trouve, elle retournera l'objet qui pointe vers cela ou ajoutera une nouvelle chaîne dans le pool.

    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

Le s1et s2sont deux objets pointant vers le pool de chaînes "Hello", et en utilisant "Hello".intern()le trouvera s1et s2. Donc "s1 == s3"renvoie true, ainsi que le s3.intern().

WendellWu
la source
Cela ne fournit pas vraiment beaucoup d'informations nouvelles. Il y a déjà une réponse exceptée.
Alexander
1

En utilisant la référence d'objet de tas si nous voulons obtenir la référence d'objet de pool constante de chaîne correspondante , nous devrions alors opter pour 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

Vue picturale entrez la description de l'image ici

Étape 1: L' objet avec les données 'Rakesh' est créé dans le pool de constantes de tas et de chaînes. De plus, s1 pointe toujours vers l'objet du tas.

Étape 2: En utilisant la référence d'objet de tas s1, nous essayons d'obtenir la référence d'objet de réserve constante de chaîne correspondante s2, en utilisant intern ()

Étape 3: Création intentionnelle d'un objet avec les données 'Rakesh' dans le pool de constantes de chaîne, référencé par le nom s3

En tant qu'opérateur "==" destiné à la comparaison de références.

Obtenir faux pour s1 == s2

Obtenir vrai pour s2 == s3

J'espère que cette aide !!

Kms
la source