193 votes

Quand devons-nous utiliser la méthode intern de String sur les littéraux de String ?

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

J'ai donc 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 s1 and s3 are same sera imprimé lorsque s3 sera interné, et s1 and s2 are same ne sera pas imprimé. Mais le résultat est : les deux lignes sont imprimées. Donc cela signifie que, par défaut, les constantes String sont internées. Mais si c'est le cas, alors pourquoi avons-nous besoin de l'option intern méthode ? En d'autres termes, quand devrions-nous utiliser cette méthode ?

16 votes

La Javadoc dont vous avez donné le lien indique également que "toutes les chaînes littérales et les expressions constantes à valeur de chaîne sont internées".

2 votes

1 votes

Pas une copie exacte

240voto

Miguel Fonseca Points 2947

Java internalise automatiquement les littéraux de type String. Cela signifie que dans de nombreux cas, l'opérateur == semble fonctionner pour les chaînes de caractères de la même manière que pour les ints ou d'autres valeurs primitives.

Étant donné que l'internage est automatique pour les littéraux de type String, la fonction intern() doit être utilisée sur les chaînes de caractères construites avec la méthode new 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.
}

reviendra :

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

Dans tous les cas, à part celui de s4 dont la valeur a été explicitement créée à l'aide de la variable new et où intern n'a pas été utilisée sur son résultat, c'est une seule instance immuable qui est retournée. Le pool de constantes de la JVM .

Se référer à Techniques Java "Égalité et internationnalisation des chaînes de caractères". pour plus d'informations.

0 votes

Je suppose que Java internalise automatiquement les chaînes de caractères à des fins d'optimisation. Il peut le faire en toute sécurité uniquement parce que les chaînes sont immuables, n'est-ce pas ?

0 votes

Je suis nouveau en Java (je viens du monde C#.NET) et je vois parfois dans un projet Java hérité "".intern() donc si je comprends bien c'est un "non-sens" aussi pour les chaînes vides.

4 votes

@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) référence le même objet (OB1) créé dans 'string Pool' Donc je peux dire que .intern() utilisée pour empêcher la création d'un nouvel objet si la même chaîne de caractères est disponible dans l'application string pool Si mon hypothèse est fausse, donnez-moi des indications.

21voto

Carl Smotricz Points 36400

Dans le cadre d'un projet récent, d'énormes structures de données ont été mises en place avec des données qui ont été lues à partir d'une base de données (et donc pas de constantes/littérales de type String) mais avec une énorme quantité de duplications. Il s'agissait d'une application bancaire, et des éléments comme les noms d'un ensemble modeste (peut-être 100 ou 200) de sociétés apparaissaient un peu partout. Les structures de données étaient déjà grandes, et si tous ces noms de sociétés avaient été des objets uniques, elles auraient débordé de la 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, ce qui permettait de gagner beaucoup d'espace.

Un autre petit avantage des chaînes internées est que == peut être utilisé (avec succès !) pour comparer des chaînes de caractères si toutes les chaînes concernées sont garanties internées. En plus de la syntaxe allégée, ceci est également une amélioration des performances. Mais comme d'autres l'ont souligné, cette façon de faire comporte un risque élevé d'introduire des erreurs de programmation, et ne devrait donc être utilisée qu'en dernier recours.

L'inconvénient est que l'internage d'une chaîne prend plus de temps que le simple fait de la jeter sur le tas, et que l'espace pour les chaînes internées peut être limité, selon l'implémentation Java. Il est préférable d'utiliser cette méthode lorsque l'on a affaire à un nombre raisonnable de chaînes de caractères avec de nombreuses duplications.

0 votes

@ 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 limited même si vous n'utilisez pas la méthode intern pour la constante String, elle sera internée automatiquement.

2 votes

@Rakesh : Il n'y a pas tant de constantes String dans une classe donnée habituellement, donc ce n'est pas un problème d'espace/temps avec les constantes.

0 votes

Oui, le commentaire de Rakesh ne s'applique pas parce que l'internalisation des chaînes de caractères n'est (explicitement) faite qu'avec des chaînes de caractères qui sont "générées" d'une manière ou d'une autre, que ce soit par une manipulation interne ou par la récupération d'une base de données ou autre. Avec les constantes, nous n'avons pas le choix.

16voto

Alexander Pogrebnyak Points 24964

Je veux ajouter mes deux cents sur l'utilisation == avec des chaînes internées.

La première chose String.equals fait est this==object .

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

Je suggère donc de ne pas s'appuyer sur le cas particulier du == pour les chaînes internées, mais utilisez toujours equals comme Gosling l'avait prévu.

EDIT : interné devient non-interné :

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 faire de l'option hasReferenceVal public, sans trop préciser qu'il attend un tableau de chaînes internées.

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, les tableaux contiennent des valeurs littérales, et parfois une chaîne non littérale est utilisée. Si equals ont été utilisés à la place de == puis hasReferenceVal aurait continué à fonctionner. Une fois encore, le gain de performance est minuscule, mais le coût de maintenance est élevé.

0 votes

"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 ?

2 votes

OK, je pensais que vous faisiez référence aux chaînes de caractères qui sortent du pool interne et se retrouvent sur le tas grâce à la magie de la JVM. Ce que tu dis, c'est que == rend plus probable certaines catégories d'erreurs de programmation.

0 votes

"Je suggère donc de ne pas se fier au cas particulier de == pour les chaînes internées, mais de toujours utiliser equals comme Gosling l'a prévu." Avez-vous une citation directe ou un commentaire de Gosling affirmant cela ? Si c'est le cas, pourquoi a-t-il pris la peine de mettre intern() et l'utilisation de == dans le langage ?

12voto

Bozho Points 273663

Les chaînes littérales et les constantes sont internées par défaut. C'est-à-dire, "foo" == "foo" (déclarés par les littéraux String), mais new String("foo") != new String("foo") .

4 votes

Donc, la question est de savoir quand il faut utiliser intern ,

0 votes

Qui a été pointé vers stackoverflow.com/questions/1833581/when-to-use-intern et un certain nombre d'autres questions, dont certaines datent d'hier.

1 votes

Laissez-moi savoir si je comprends bien cette déclaration : String literals and constants are interned by default est correcte. new String("foo") --> Ici, une chaîne littérale "foo" est créée dans le pool de chaînes et une dans le tas, ce qui fait que deux objets sont créés.

2voto

lifelen Points 18

Les chaînes de caractères internées évitent les chaînes de caractères en double. L'internage permet d'économiser de la RAM au prix d'un temps de calcul plus important pour détecter et remplacer les chaînes en double. Il n'existe qu'une seule copie de chaque chaîne de caractères internée, quel que soit le nombre de références qui y font référence. Les chaînes étant immuables, si deux méthodes différentes utilisent accidentellement la même chaîne, elles peuvent partager une copie de cette chaîne. Le processus de conversion des chaînes dupliquées en chaînes partagées s'appelle interne.String.intern() vous donne l'adresse de la chaîne maître canonique. Vous pouvez comparer les chaînes internées avec un simple == (qui compare les pointeurs) au lieu de est égal à qui compare les caractères de la chaîne un par un. Les chaînes étant immuables, le processus interne est libre d'économiser encore plus d'espace, par exemple en ne créant pas de littéral de chaîne 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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X