web-dev-qa-db-fra.com

Immutabilité des chaînes en Java

Prenons l'exemple suivant.

String str = new String();

str  = "Hello";
System.out.println(str);  //Prints Hello

str = "Help!";
System.out.println(str);  //Prints Help!

Maintenant, en Java, les objets String sont immuables. Alors comment se fait-il que l'objet str puisse se voir attribuer la valeur "Aide!". Cela ne contredit-il pas l'immuabilité des chaînes de caractères en Java? Quelqu'un peut-il m'expliquer le concept exact d'immutabilité?

Modifier:

D'accord. Je comprends maintenant, mais juste une question de suivi. Qu'en est-il du code suivant: 

String str = "Mississippi"; 
System.out.println(str); // prints Mississippi 

str = str.replace("i", "!"); 
System.out.println(str); // prints M!ss!ss!pp! 

Est-ce que cela signifie que deux objets sont à nouveau créés ("Mississippi" et "M! Ss! Ss! Pp!") Et que la référence str pointe vers un objet différent après la méthode replace() 

193
Light_handle

str n'est pas un objet, c'est une référence à un objet. "Hello" et "Help!" sont deux objets String distincts. Ainsi, str pointe vers une chaîne. Vous pouvez changer ce qu'il pointe vers , mais pas ce sur quoi il pointe (.

Prenez ce code, par exemple:

String s1 = "Hello";
String s2 = s1;
// s1 and s2 now point at the same string - "Hello"

Maintenant, il n'y a rien1 nous pourrions faire à s1 qui affecterait la valeur de s2. Ils font référence au même objet - la chaîne "Hello" - mais cet objet est immuable et ne peut donc pas être modifié.

Si nous faisons quelque chose comme ça:

s1 = "Help!";
System.out.println(s2); // still prints "Hello"

Nous voyons ici la différence entre la mutation d'un objet et la modification d'une référence. s2 pointe toujours sur le même objet que celui initialement défini sur s1. Régler s1 sur "Help!" ne modifie que la référence , tandis que l'objet String auquel il faisait référence à l'origine reste inchangé.

Si les chaînes étaient mutables, nous pourrions faire quelque chose comme ceci:

String s1 = "Hello";
String s2 = s1;
s1.setCharAt(1, 'a'); // Fictional method that sets character at a given pos in string
System.out.println(s2); // Prints "Hallo"

Modifier pour répondre à la modification de l'OP:

Si vous regardez le code source de String.replace (char, char) (également disponible dans src.Zip dans votre répertoire d’installation du JDK - un conseil pro est de regarder à chaque fois que vous vous demandez comment quelque chose de vraiment fonctionne) vous pouvez voir que ce qu'il fait est le suivant:

  • S'il existe une ou plusieurs occurrences de oldChar dans la chaîne en cours, effectuez une copie de la chaîne en cours en remplaçant toutes les occurrences de oldChar par newChar.
  • Si oldChar n'est pas présent dans la chaîne en cours, retournez la chaîne en cours.

Alors oui, "Mississippi".replace('i', '!') crée un nouvel objet String. Encore une fois, ce qui suit est valable:

String s1 = "Mississippi";
String s2 = s1;
s1 = s1.replace('i', '!');
System.out.println(s1); // Prints "M!ss!ss!pp!"
System.out.println(s2); // Prints "Mississippi"
System.out.println(s1 == s2); // Prints "false" as s1 and s2 are two different objects

Votre devoir pour le moment est de voir ce que le code ci-dessus fait si vous changez s1 = s1.replace('i', '!'); en s1 = s1.replace('Q', '!'); :)


1 En fait, il est possible de muter des chaînes (et d’autres objets immuables). Cela nécessite une réflexion, est très dangereux et ne devrait jamais être utilisé à moins que vous ne souhaitiez réellement détruire le programme.

293
gustafc

L'objet que les références str peuvent changer, mais les objets String eux-mêmes ne le peuvent pas.

Les objets String contenant la chaîne "Hello" et "Help!" ne peuvent pas changer leurs valeurs, ils sont donc immuables.

L'immuabilité des objets String ne signifie pas que les références pointant vers l'objet ne peuvent pas changer.

Une façon d'éviter que la référence str ne soit modifiée consiste à la déclarer comme étant final:

final String STR = "Hello";

Maintenant, essayer d’affecter une autre String à STR provoquera une erreur de compilation.

22
coobird

Light_handle Je vous recommande de lire Cup Size - une histoire sur les variables et Pass-by-Value Please (Taille de la coupe suite) . Cela aidera beaucoup en lisant les articles ci-dessus. 

Avez-vous les lire? Oui. Bien. 

String str = new String();

Cela crée un nouveau "contrôle à distance" appelé "str" et lui attribue la valeur new String() (ou "").

par exemple. en mémoire cela crée:

str --- > ""

str  = "Hello";

Ceci modifie ensuite la télécommande "str" mais ne modifie pas la chaîne d'origine "".

par exemple. en mémoire cela crée:

str -+   ""
     +-> "Hello"

str = "Help!";

Cela modifie ensuite le contrôle à distance "str" mais ne modifie pas la chaîne d'origine "" ni l'objet sur lequel le contrôle à distance pointe actuellement.

par exemple. en mémoire cela crée:

str -+   ""
     |   "Hello"
     +-> "Help!"
9

Permet de le casser en quelques parties

String s1 = "hello";

Cette instruction crée une chaîne contenant hello et occupe de la place dans la mémoire, c'est-à-dire dans Constant String Pool , et l'a affectée à l'objet de référence s1

String s2 = s1;

Cette instruction assigne la même chaîne hello à la nouvelle référence s2

         __________
        |          |
s1 ---->|  hello   |<----- s2
        |__________| 

Les deux références pointant vers la même chaîne, indiquez la même valeur comme suit.

out.println(s1);    // o/p: hello
out.println(s2);    // o/p: hello

Bien que String soit immuable , une affectation est possible, le s1 se réfère maintenant à la nouvelle valeur stack .

s1 = "stack";    
         __________
        |          |
s1 ---->|  stack   |
        |__________|

Mais que dire de s2 objet qui pointe vers bonjour il sera tel quel.

         __________
        |          |
s2 ---->|  hello   |
        |__________|

out.println(s1);    // o/p: stack
out.println(s2);    // o/p: hello

Puisque String est immuable Java Virtual Machine ne nous permettra pas de modifier la chaîne s1 par sa méthode. Il créera tous les nouveaux objets String dans le pool comme suit.

s1.concat(" overflow");

                 ___________________
                |                   |
s1.concat ----> |  stack overflow   |
                |___________________|

out.println(s1);    // o/p: stack
out.println(s2);    // o/p: hello
out.println(s1.concat); // o/p: stack overflow

Notez que si String serait mutable, la sortie aurait été 

out.println(s1);    // o/p: stack overflow

Maintenant, vous pourriez être surpris de savoir pourquoi String a des méthodes telles que concat () à modifier. L'extrait suivant effacera votre confusion. 

s1 = s1.concat(" overflow");

Ici, nous assignons la valeur modifiée de chaîne à s1 référence.

         ___________________
        |                   |
s1 ---->|  stack overflow   |
        |___________________|


out.println(s1);    // o/p: stack overflow
out.println(s2);    // o/p: hello

C'est pourquoi Java a décidé que String serait une classe finale Sinon, tout le monde peut modifier et changer la valeur de string . J'espère que cela vous aidera un petit peu.

7
Shailesh Sonare

L'objet de chaîne qui a été référencé pour la première fois par str n'a pas été modifié. Tout ce que vous avez fait est de faire en sorte que str fasse référence à un nouvel objet de chaîne.

6
daveb

En ce qui concerne la partie remplacement de votre question, essayez ceci:

String str = "Mississippi"; 
System.out.println(str); //Prints Mississippi 

String other = str.replace("i", "!"); 
System.out.println(str); //still prints Mississippi 
System.out.println(other);  // prints M!ss!ss!pp!
5
Carlos Heuberger

La chaîne ne changera pas, la référence à elle changera. Vous confondez l'immuabilité avec le concept de champs final. Si un champ est déclaré en tant que final, il ne peut pas être réaffecté une fois qu’il a été affecté.

5
akf

L'immuabilité implique que la valeur d'un objet instancié ne puisse pas changer, vous ne pouvez jamais transformer "Bonjour" en "Aide!".

La variable str est une référence à un objet. Lorsque vous affectez une nouvelle valeur à str, vous ne modifiez pas la valeur de l'objet référencé, vous référencez un autre objet.

3
Preston Guillot

Bien que Java tente de l'ignorer, str n'est rien d'autre qu'un pointeur. Cela signifie que lorsque vous écrivez pour la première fois str = "Hello";, vous créez un objet sur lequel str pointe. Lorsque vous réaffectez str en écrivant str = "Help!";, un nouvel objet est créé et l'ancien objet "Hello" est récupéré lorsque Java le souhaite.

3
twolfe18

Comme Linus Tolvards a dit:

Parler n'est pas cher. Montre moi le code

Regarde ça:

public class Test{
    public static void main(String[] args){

        String a = "Mississippi";
        String b = "Mississippi";//String immutable property (same chars sequence), then same object

        String c = a.replace('i','I').replace('I','i');//This method creates a new String, then new object
        String d = b.replace('i','I').replace('I','i');//At this moment we have 3 String objects, a/b, c and d

        String e = a.replace('i','i');//If the arguments are the same, the object is not affected, then returns same object

        System.out.println( "a==b? " + (a==b) ); // Prints true, they are pointing to the same String object

        System.out.println( "a: " + a );
        System.out.println( "b: " + b );

        System.out.println( "c==d? " + (c==d) ); // Prints false, a new object was created on each one

        System.out.println( "c: " + c ); // Even the sequence of chars are the same, the object is different
        System.out.println( "d: " + d );

        System.out.println( "a==e? " + (a==e) ); // Same object, immutable property
    }
}

La sortie est

a==b? true
a: Mississippi
b: Mississippi
c==d? false
c: Mississippi
d: Mississippi
a==e? true

Alors, rappelez-vous deux choses:

  • Les chaînes sont immuables jusqu'à ce que vous appliquiez une méthode qui manipule et crée une nouvelle (cas c & d).
  • La méthode Replace renvoie le même objet String si les deux paramètres sont identiques.
2
etlapa

Utilisation:

String s = new String("New String");
s.concat(" Added String");
System.out.println("String reference -----> "+s); // Output: String reference -----> New String

Si vous voyez ici, j'utilise la méthode concat pour modifier la chaîne d'origine, c'est-à-dire "New String" avec une chaîne "Added String", mais j'ai toujours la sortie comme précédente, ce qui prouve qu'il est impossible de modifier objet de la classe String, mais si vous faites cela par la classe StringBuilder, cela fonctionnera. Il est énuméré ci-dessous.

StringBuilder sb = new StringBuilder("New String");
sb.append(" Added String");
System.out.println("StringBuilder reference -----> "+sb);// Output: StringBuilder reference -----> New String Added String
2
Nihar nayak

La classe String est immuable et vous ne pouvez pas changer la valeur de l'objet immuable . Mais dans le cas de String, si vous modifiez la valeur de string, cela créera une nouvelle chaîne dans le pool de chaînes et que votre référence à cette valeur un plus vieux. donc, de cette façon, la chaîne est immuable . Prenons votre exemple,

String str = "Mississippi";  
System.out.println(str); // prints Mississippi 

il créera une chaîne "Mississippi" et l'ajoutera au pool de chaînesso est à présent pointé vers Mississippi.

str = str.replace("i", "!");  
System.out.println(str); // prints M!ss!ss!pp! 

Mais après l'opération ci-dessus, une chaîne sera créée une autre fois "M! Ss! Ss! Pp!" et il sera ajouté au pool de chaînes. et maintenant str désigne M! ss! ss! pp !, pas le Mississippi.

ainsi, lorsque vous modifierez la valeur d'un objet string, il en créera un autre et l'ajoutera au pool de chaînes.

Donnons un autre exemple 

String s1 = "Hello"; 
String s2 = "World"; 
String s = s1 + s2;

cette ligne au-dessus de trois ajoutera trois objets de chaîne à un groupe de chaînes.
1) Bonjour
2) Monde
3) HelloWorld

2
Nirav Chhatrola

Comme String est immuable, les modifications ne se produiront pas si vous n'affectez pas la valeur de fonction renvoyée à string.so, dans votre question, attribuez la valeur de la fonction swap à la valeur s.

s = swap (s, n1, n2); la valeur de la chaîne s changera alors.

J'obtenais également la valeur inchangée lorsque j'écrivais le programme pour obtenir une chaîne de permutations (Bien que cela ne donne pas toutes les permutations mais c'est par exemple pour répondre à votre question)

Voici un exemple.

> import Java.io.*;  public class MyString { public static void
> main(String []args)throws IOException {  BufferedReader br=new
> BufferedReader(new InputStreamReader(System.in));  String
> s=br.readLine().trim(); int n=0;int k=0;  while(n!=s.length()) {
> while(k<n){  swap(s,k,n); System.out.println(s); swap(s,k,n); k++; }
> n++; } }  public static void swap(String s,int n1,int n2) { char temp;
> temp=s.charAt(n1); StringBuilder sb=new StringBuilder(s);
> sb.setCharAt(n1,s.charAt(n2)); sb.setCharAt(n2,temp); s=sb.toString();
> } }

mais je ne recevais pas les valeurs permutées de la chaîne de code ci-dessus.Alors j'ai assigné la valeur renvoyée de la fonction swap à la chaîne et ai changé les valeurs de chaîne. après avoir attribué la valeur renvoyée, j’ai obtenu les valeurs permutées de chaîne.

/import Java.util.*; import Java.io.*; public class MyString { public static void main(String []args)throws IOException{
BufferedReader br=new BufferedReader(new InputStreamReader(System.in)); 
String s=br.readLine().trim(); int n=0;int k=0; 
while(n!=s.length()){ while(k<n){ s=swap(s,k,n); 
System.out.println(s); s=swap(s,k,n); k++; } n++; } } 
public static String swap(String s,int n1,int n2){
char temp; temp=s.charAt(n1); StringBuilder sb=new StringBuilder(s); sb.setCharAt(n1,s.charAt(n2)); sb.setCharAt(n2,temp); s=sb.toString(); return s; } }
0
Shivam Shukla

Ou vous pouvez essayer:

public class Tester
{
public static void main(String[] args)
{
 String str = "Mississippi"; 
 System.out.println(str); // prints Mississippi 
 System.out.println(str.hashCode());

 str = str.replace("i", "!"); 
 System.out.println(str); // prints M!ss!ss!pp! 
 System.out.println(str.hashCode());
 }
 }

Cela montrera comment le hashcode change.

0
user1554868

je l'expliquerais avec un exemple simple


considérer n'importe quel tableau de caractères: par exemple char a [] = {'h', 'e', ​​'l', 'l', 'o'}; et une chaîne: String s = "hello";


sur un tableau de caractères, nous pouvons effectuer des opérations comme imprimer seulement les trois dernières lettres en itérant le tableau; mais dans string nous devons créer un nouvel objet String et copier la sous-chaîne requise et son adresse sera dans le nouvel objet string.

par exemple.

***String s="hello";
String s2=s.substrig(0,3);***

donc s2 aura "hel";

0
Aadesh Nirfarake

Ici, immuabilité signifie que l'instance peut pointer vers une autre référence, mais que le contenu original de la chaîne ne serait pas modifié à la référence d'origine . Laissez-moi vous expliquer par le premier exemple que vous avez donné . First str pointe vers "Hello". , son Ok jusqu'à ce ..___. Deuxième fois, il pointe sur "Aide!". Ici str a commencé à pointer vers "Aide!" et la référence à la chaîne "Hello" est perdue et nous ne pouvons pas la récupérer.

En fait, lorsque str essaiera de modifier le contenu existant, une autre nouvelle chaîne sera générée et str commencera à pointer sur cette référence . Nous voyons donc que la chaîne à la référence d'origine n'est pas modifiée mais qu'elle est sûre à la référence et une instance d'objet a commencé à pointer vers une référence différente pour que l'immuabilité soit préservée.

0
Afzal Ahmad

Si HELLO est votre chaîne, vous ne pouvez pas changer HELLO en HILLO. Cette propriété est appelée propriété d'immutabilité. 

Vous pouvez avoir plusieurs variables de chaîne de pointeur pour pointer HELLO String. 

Mais si HELLO est char Array, vous pouvez remplacer HELLO par HILLO. Par exemple, 

char[] charArr = 'HELLO';
char[1] = 'I'; //you can do this

Les langages de programmation ont des variables de données immuables, ce qui permet de les utiliser comme clés dans une paire clé/valeur. 

0
Uddhav Gautam

L’immuabilité que je peux dire est que vous ne pouvez pas changer la chaîne elle-même. Supposons que vous avez String x, dont la valeur est "abc". Maintenant, vous ne pouvez pas changer la chaîne, c’est-à-dire que vous ne pouvez changer aucun caractère/s dans "abc".

Si vous devez modifier un ou plusieurs caractères de la chaîne, vous pouvez utiliser un tableau de caractères et le transformer ou utiliser StringBuilder.

String x = "abc";
x = "pot";
x = x + "hj";
x = x.substring(3);
System.out.println(x);

char x1[] = x.toCharArray();
x1[0] = 's';
String y = new String(x1);
System.out.println(y);

Sortie:

hj
sj
0
Sumedha Khatter

Très tard à la réponse, mais je voulais mettre un message concis de l'auteur de la classe String en Java 

Les chaînes sont constantes. leurs valeurs ne peuvent plus être changées après créé. Les tampons de chaîne supportent les chaînes mutables. Parce que String les objets sont immuables, ils peuvent être partagés.

Il est possible de déduire de cette documentation que tout ce qui change de chaîne retourne un objet different (qui peut être nouveau ou interné et ancien) ..__ La remarque moins subtile à ce sujet devrait provenir de la signature de la fonction .Pensez-y, 'Pourquoi ont-ils fait en sorte qu'une fonction sur un objet retourne un objet au lieu d'un statut?'.

public String replace(char oldChar, char newChar) 

Également une source supplémentaire qui rend ce comportement explicite (From replace function documentation)

Retourne une nouvelle chaîne résultant du remplacement de toutes les occurrences de oldChar dans cette chaîne avec newChar.

Source: https://docs.Oracle.com/javase/7/docs/api/Java/lang/String.html#replace(char,%20char)

  • auteur Lee Boynton
  • auteur Arthur van Hoff
  • auteur Martin Buchholz
  • auteur Ulf Zibis

Source: JavaDoc de String.

0
Ajeet Ganga

En Java, les objets sont généralement accessibles via des références. Dans votre code, str est une référence qui est d'abord affectée à "Hello" (un objet créé automatiquement ou extrait du constant pool ), puis un autre objet "Aide!" à même référence. Un point à noter est que la référence est la même et modifiée, mais les objets sont différents. Une dernière chose dans votre code, vous avez accédé à trois objets,

  1. Lorsque vous avez appelé new String ().
  2. Lorsque vous avez attribué "bonjour".
  3. Quand vous avez assigné "aide!".

L'appel de new String () crée un nouvel objet même s'il existe dans un pool de chaînes. Par conséquent, il ne doit généralement pas être utilisé. Pour placer une chaîne créée à partir de new String () dans le pool de chaînes, vous pouvez essayer la méthode intern().

J'espère que ça aide.

0
Nayan Srivastava

La chaîne Object - les méthodes elles-mêmes sont faites pour être "immuables" . Cette action ne produit aucun changement: "letters.replace (" bbb "," aaa ");" 

Cependant, l’attribution de données entraîne des modifications du contenu de Strings:

    letters = "aaa";
    letters=null;
    System.out.println(letters);
    System.out.println(oB.hashCode());
    System.out.println(letters);
    letters = "bbbaaa";
    System.out.println(oB.hashCode());
    System.out.println(letters);

// Le hashcode de la chaîne Object ne change pas.

0
Loshen Naicker

String en Java dans Immutable and Final signifie simplement qu'il ne peut être ni modifié, ni modifié:

Cas 1:

class TestClass{  
 public static void main(String args[]){  
   String str = "ABC";  
   str.concat("DEF");  
   System.out.println(str);  
 }  
} 

Sortie: ABC

Raison: la référence à l'objet str n'est pas modifiée. Il s'agit en fait d'un nouvel objet "DEF" est créé. Il se trouve dans le pool et n’a aucune référence (c'est-à-dire perdu).

Cas 2:

class TestClass{  
 public static void main(String args[]){  
   String str="ABC";  
   str=str.concat("DEF");  
   System.out.println(str);  
 }  
}  

Sortie: ABCDEF

Raison: dans ce cas, str fait maintenant référence à un nouvel objet "ABCDEF" par conséquent, il imprime ABCDEF, c'est-à-dire que l'objet str précédent "ABC" est perdu dans le pool sans référence.

0
Virtual

String est immuable. Ce qui signifie que nous ne pouvons que changer le reference.


String a = "a";
System.out.println("String a is referencing to "+a); // Output: a

a.concat("b");
System.out.println("String a is referencing to "+a); // Output: a

a = a.concat("b");
System.out.println("String a has created a new reference and is now referencing to "+a); // Output: ab
0
DeathRs

La chaîne est immuable signifie que vous ne pouvez pas modifier l'objet lui-même, mais vous pouvez modifier la référence à l'objet. Lorsque vous avez appelé un = "ty", vous changez la référence d'un a en un nouvel objet créé par le littéral String "ty". Changer un objet signifie utiliser ses méthodes pour changer un de ses champs (ou les champs sont publics et non finaux, afin qu'ils puissent être mis à jour de l'extérieur sans y accéder via des méthodes), par exemple:

Foo x = new Foo("the field");
x.setField("a new field");
System.out.println(x.getField()); // prints "a new field"

Alors que dans une classe immuable (déclarée comme finale, pour empêcher toute modification via l'héritage) (ses méthodes ne peuvent pas modifier ses champs, et les champs sont toujours privés et il est recommandé de les définir comme final), par exemple String, vous ne pouvez pas modifier le String actuel, mais vous peut renvoyer une nouvelle chaîne, c'est-à-dire:

String s = "some text";
s.substring(0,4);
System.out.println(s); // still printing "some text"
String a = s.substring(0,4);
System.out.println(a); // prints "some"
0
E2rabi

Pour ceux qui se demandent comment casser l'immuabilité de String en Java ...

Code

import Java.lang.reflect.Field;

public class StringImmutability {
    public static void main(String[] args) {
        String str1 = "I am immutable";
        String str2 = str1;

        try {
            Class str1Class = str1.getClass();
            Field str1Field = str1Class.getDeclaredField("value");

            str1Field.setAccessible(true);
            char[] valueChars = (char[]) str1Field.get(str1);

            valueChars[5] = ' ';
            valueChars[6] = ' ';

            System.out.println(str1 == str2);
            System.out.println(str1);
            System.out.println(str2);           
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }
}

Sortie

true
I am   mutable
I am   mutable
0
martynas