Question

C’est peut-être la question la plus stupide jamais posée, mais je pense que c’est assez déroutant pour un débutant en Java.

  1. Quelqu'un peut-il préciser le sens de immuable ?
  2. Pourquoi une String est-elle immuable?
  3. Quels sont les avantages / inconvénients des objets immuables?
  4. Pourquoi préférer un objet mutable tel que StringBuilder à String et vice-verse?

Un bel exemple (en Java) sera vraiment apprécié.

Était-ce utile?

La solution

Immutable signifie qu’une fois que le constructeur d’un objet a été exécuté, cette instance ne peut plus être modifiée.

Cela est utile car cela signifie que vous pouvez transmettre des références à l'objet sans vous soucier du fait que quelqu'un d'autre va en modifier le contenu. Il n’existe aucun problème de verrouillage avec les objets qui ne changent jamais

surtout en ce qui concerne la simultanéité.

par exemple

class Foo
{
     private final String myvar;

     public Foo(final String initialValue)
     {
         this.myvar = initialValue;
     }

     public String getValue()
     {
         return this.myvar;
     }
}

Foo n'a pas à craindre que l'appelant de getValue () puisse modifier le texte de la chaîne.

Si vous imaginez une classe similaire à Foo , mais avec un StringBuilder plutôt qu'un String en tant que membre, vous pouvez voir qu'un l’appelant de getValue () pourra modifier l’attribut StringBuilder d’une instance Foo .

Méfiez-vous également des différents types d’immuabilité que vous pourriez trouver: Eric Lippert a écrit un article de blog à ce sujet. Fondamentalement, vous pouvez avoir des objets dont l'interface est immuable, mais dans les coulisses, un état privé mutable réel (et ne peut donc pas être partagé en toute sécurité entre les threads).

Autres conseils

Un objet immuable est un objet pour lequel les champs internes (ou au moins tous les champs internes qui affectent son comportement externe) ne peuvent pas être modifiés.

Les chaînes immuables présentent de nombreux avantages:

Performances: procédez comme suit:

String substring = fullstring.substring(x,y);

Le C sous-jacent de la méthode substring () ressemble probablement à ceci:

// Assume string is stored like this:
struct String { char* characters; unsigned int length; };

// Passing pointers because Java is pass-by-reference
struct String* substring(struct String* in, unsigned int begin, unsigned int end)
{
    struct String* out = malloc(sizeof(struct String));
    out->characters = in->characters + begin;
    out->length = end - begin;
    return out;
}

Notez que aucun des caractères ne doit être copié! Si l'objet String était modifiable (les caractères pourraient changer ultérieurement), vous devrez alors copier tous les caractères, sinon les modifications apportées aux caractères dans la sous-chaîne sera reflétée plus tard dans l'autre chaîne.

Concurrence : si la structure interne d'un objet immuable est valide, elle le sera toujours. Il n'y a aucune chance que différents threads puissent créer un état non valide dans cet objet. Par conséquent, les objets immuables sont Thread Safe .

Collecte des ordures ménagères: il est beaucoup plus facile au dépanneur de prendre des décisions logiques concernant les objets immuables.

Cependant, l'immuabilité présente également des inconvénients:

Performances: Attendez, je pensais que la performance était un avantage de l’immutabilité! Eh bien, c'est parfois, mais pas toujours. Prenez le code suivant:

foo = foo.substring(0,4) + "a" + foo.substring(5);  // foo is a String
bar.replace(4,5,"a"); // bar is a StringBuilder

Les deux lignes remplacent le quatrième caractère par la lettre "a". Non seulement le deuxième morceau de code est plus lisible, mais plus rapide. Regardez comment vous devez faire le code sous-jacent pour foo. Les sous-chaînes sont faciles, mais maintenant, comme il y a déjà un caractère dans l'espace cinq et que quelque chose d'autre pourrait faire référence à foo, vous ne pouvez pas simplement le changer; vous devez copier la chaîne entière (bien sûr, certaines de ces fonctionnalités sont résumées dans des fonctions du C réel sous-jacent, mais le but ici est de montrer le code exécuté en un seul endroit).

struct String* concatenate(struct String* first, struct String* second)
{
    struct String* new = malloc(sizeof(struct String));
    new->length = first->length + second->length;

    new->characters = malloc(new->length);

    int i;

    for(i = 0; i < first->length; i++)
        new->characters[i] = first->characters[i];

    for(; i - first->length < second->length; i++)
        new->characters[i] = second->characters[i - first->length];

    return new;
}

// The code that executes
struct String* astring;
char a = 'a';
astring->characters = &a;
astring->length = 1;
foo = concatenate(concatenate(slice(foo,0,4),astring),slice(foo,5,foo->length));

Notez que la concaténation est appelée deux fois , ce qui signifie que toute la chaîne doit être bouclée! Comparez cela au code C pour l'opération bar :

bar->characters[4] = 'a';

L'opération de chaîne mutable est évidemment beaucoup plus rapide.

En conclusion: dans la plupart des cas, vous souhaitez une chaîne immuable. Mais si vous devez ajouter et insérer beaucoup dans une chaîne, vous avez besoin de la mutabilité pour plus de rapidité. Si vous souhaitez bénéficier des avantages de la sécurité d'accès concurrentiel et de la récupération de place, la clé consiste à conserver vos objets mutables au sein d'une méthode:

// This will have awful performance if you don't use mutable strings
String join(String[] strings, String separator)
{
    StringBuilder mutable;
    boolean first = true;

    for(int i = 0; i < strings.length; i++)
    {
        if(!first) first = false;
        else mutable.append(separator);

        mutable.append(strings[i]);
    }

    return mutable.toString();
}

L'objet mutable étant une référence locale, vous n'avez pas à vous soucier de la sécurité d'accès simultané (un seul thread le touche jamais). Et comme il n'est référencé nulle part ailleurs, il est uniquement alloué sur la pile. Il est donc désalloué dès que l'appel de fonction est terminé (vous n'avez pas à vous soucier de la récupération de place). Et vous bénéficiez de tous les avantages en termes de performances de la mutabilité et de l'immuabilité.

En réalité, String n'est pas immuable si vous utilisez la définition de wikipedia proposée ci-dessus.

L'état de la chaîne change après la construction. Jetez un coup d'œil à la méthode hashcode (). String met en cache la valeur hashcode dans un champ local mais ne la calcule pas avant le premier appel de hashcode (). Cette évaluation paresseuse de hashcode place String dans une position intéressante en tant qu’objet immuable dont l’état change, mais on ne peut pas constater qu’il a changé sans utiliser la réflexion.

Donc, peut-être que la définition de immuable devrait être un objet qui ne peut pas être observé comme ayant changé.

Si l'état d'un objet immuable change après sa création mais que personne ne peut le voir (sans réflexion), l'objet est-il toujours immuable?

Les objets immuables sont des objets qui ne peuvent pas être modifiés par programmation. Ils sont particulièrement utiles dans les environnements multithreads ou dans d’autres environnements où plusieurs processus peuvent modifier (transformer) les valeurs d’un objet.

Juste pour préciser, StringBuilder est en réalité un objet modifiable, et non immuable. Une chaîne java standard est immuable (ce qui signifie qu'une fois créée, vous ne pouvez pas modifier la chaîne sous-jacente sans modifier l'objet).

Par exemple, supposons que j'ai une classe appelée ColoredString qui a une valeur String et une couleur String:

public class ColoredString {

    private String color;
    private String string;

    public ColoredString(String color, String string) {
        this.color  = color;
        this.string = string;
    }

    public String getColor()  { return this.color;  }
    public String getString() { return this.string; }

    public void setColor(String newColor) {
        this.color = newColor;
    }

}

Dans cet exemple, ColoredString est dit mutable car vous pouvez modifier (muter) l'une de ses propriétés de clé sans créer de nouvelle classe ColoredString. La raison pour laquelle ceci peut être mauvais est, par exemple, supposons que vous ayez une application graphique qui comporte plusieurs threads et que vous utilisiez ColoredStrings pour imprimer des données dans la fenêtre. Si vous avez une instance de ColoredString créée en tant que

new ColoredString("Blue", "This is a blue string!");

Vous vous attendez alors à ce que la chaîne soit toujours "Blue". Si un autre thread, cependant, a mis la main sur cette instance et a appelé

blueString.setColor("Red");

Vous auriez soudainement et probablement de manière inattendue un code "Rouge". chaîne lorsque vous voulez un " Blue " un. De ce fait, les objets immuables sont presque toujours préférés lors du passage d'instances d'objets. Lorsque vous avez un cas où des objets mutables sont vraiment nécessaires, vous devez généralement protéger cet objet en ne transmettant que des copies de votre champ de contrôle spécifique.

Pour récapituler, java.lang.String est un objet immuable en Java (il ne peut pas le changer une fois créé) et java.lang.StringBuilder est un objet modifiable car il peut être modifié sans créer une nouvelle instance.

  1. Dans les grandes applications, il est courant que les littéraux de chaîne occupent une grande quantité de mémoire. Pour gérer efficacement la mémoire, la machine virtuelle Java alloue une zone appelée "pool constant de chaînes". ( Notez qu'en mémoire, même une chaîne non référencée contient un caractère char [], un int pour sa longueur et un autre pour son hashCode. Pour un nombre, en revanche, un maximum de huit octets immédiats est obligatoire )
  2. Lorsque le compliant rencontre un littéral String, il vérifie le pool pour voir si un littéral identique est déjà présent. Et s'il en trouve un, la référence au nouveau littéral est dirigée vers la chaîne existante et aucun nouvel "objet littéral de chaîne" n'est créé (la chaîne existante obtient simplement une référence supplémentaire).
  3. Par conséquent: la mutabilité des chaînes économise de la mémoire ...
  4. Mais quand l'une des variables change de valeur, en fait, c'est uniquement leur référence qui a changé, pas la valeur en mémoire (par conséquent, cela n'affectera pas les autres variables qui la référence), comme indiqué ci-dessous ....

Chaîne s1 = "Ancienne chaîne" ;;

//s1 variable, refers to string in memory
        reference                 |     MEMORY       |
        variables                 |                  |

           [s1]   --------------->|   "Old String"   |

Chaîne s2 = s1;

//s2 refers to same string as s1
                                  |                  |
           [s1]   --------------->|   "Old String"   |
           [s2]   ------------------------^

s1 = "Nouvelle chaîne" ;;

//s1 deletes reference to old string and points to the newly created one
           [s1]   -----|--------->|   "New String"   |
                       |          |                  |
                       |~~~~~~~~~X|   "Old String"   |
           [s2]   ------------------------^
  

La chaîne originale 'en mémoire' n'a pas changé, mais le   La variable de référence a été modifiée afin de faire référence à la nouvelle chaîne.   Et si nous n'avions pas s2, " Old String " serait toujours dans la mémoire mais   nous ne pourrons pas y accéder ...

" immuable " signifie que vous ne pouvez pas changer de valeur. Si vous avez une instance de la classe String, toute méthode que vous appelez et qui semble modifier la valeur créera en réalité une autre chaîne.

String foo = "Hello";
foo.substring(3);
<-- foo here still has the same value "Hello"

Pour préserver les modifications, vous devriez faire quelque chose comme ceci.     foo = foo.sustring (3);

Immuable vs mutable peut être amusant lorsque vous travaillez avec des collections. Pensez à ce qui se passera si vous utilisez un objet mutable comme clé de mappage, puis modifiez la valeur (astuce: pensez à est égal à et à hashCode ).

java.time

Il est peut-être un peu tardif, mais pour comprendre ce qu'est un objet immuable, considérons l'exemple suivant tiré de la nouvelle API Java 8 Date and Time ( java.time ). Comme vous le savez probablement, tous les objets de date de Java 8 sont immuables . Dans l'exemple suivant,

LocalDate date = LocalDate.of(2014, 3, 18); 
date.plusYears(2);
System.out.println(date);

Sortie:

  

2014-03-18

Ceci imprime la même année que la date initiale car le plusYears (2) renvoie un nouvel objet, de sorte que l'ancienne date reste inchangée car il s'agit d'un objet immuable. Une fois créé, vous ne pouvez plus le modifier et la variable de date pointe toujours dessus.

Cet exemple de code devrait donc capturer et utiliser le nouvel objet instancié et renvoyé par cet appel à plusYears .

LocalDate date = LocalDate.of(2014, 3, 18); 
LocalDate dateAfterTwoYears = date.plusYears(2);
  

date.toString ()… 2014-03-18

     

dateAfterTwoYears.toString ()… 2016-03-18

J'aime beaucoup les explications fournies par le guide d'étude du programmeur certifié Sun pour Java 5 de SCJP .

  

Pour rendre Java plus efficace en termes de mémoire, la machine virtuelle Java a mis de côté une zone de mémoire spéciale appelée groupe "Chaîne constante String". Lorsque le compilateur rencontre un littéral String, il vérifie le pool pour voir si une chaîne identique existe déjà. Si une correspondance est trouvée, la référence au nouveau littéral est dirigée vers la chaîne existante et aucun nouvel objet littéral String n'est créé.

L'état des objets immuables ne peut pas être modifié après leur création.

Il existe trois raisons principales d'utiliser des objets immuables chaque fois que vous le pouvez. Toutes contribueront à réduire le nombre de bogues introduits dans votre code:

  • Il est beaucoup plus facile de raisonner sur le fonctionnement de votre programme lorsque vous savez que l'état d'un objet ne peut pas être modifié par une autre méthode
  • Les objets immuables sont automatiquement thread-safe (en supposant qu'ils soient publiés de manière sécurisée), ils ne seront donc jamais la cause de ces bogues multithreading difficiles à cerner
  • Les objets immuables auront toujours le même code de hachage, ils peuvent donc être utilisés comme clés dans un HashMap (ou similaire). Si le code de hachage d'un élément d'une table de hachage devait changer, l'entrée de la table serait alors perdue, car les tentatives pour le trouver dans la table finiraient par chercher au mauvais endroit. C’est la raison principale pour laquelle les objets String sont immuables: ils sont fréquemment utilisés comme clés HashMap.

Vous pouvez également effectuer d'autres optimisations dans le code lorsque vous savez que l'état d'un objet est immuable - la mise en cache du hachage calculé, par exemple -, mais il s'agit d'optimisations et par conséquent pas si intéressantes.

Un sens est lié à la manière dont la valeur est stockée dans l'ordinateur. Pour une chaîne .Net par exemple, cela signifie que la chaîne en mémoire ne peut pas être changée. Lorsque vous pensez la modifier, vous êtes en fait créer une nouvelle chaîne en mémoire et pointer la variable existante (qui est simplement un pointeur sur la collection de caractères ailleurs) sur la nouvelle chaîne.

String s1="Hi";
String s2=s1;
s1="Bye";

System.out.println(s2); //Hi  (if String was mutable output would be: Bye)
System.out.println(s1); //Bye

s1 = "Hi" : un objet s1 a été créé avec "Hi". valeur en elle.

s2 = s1 : un objet s2 est créé en référence à l'objet s1.

s1 = "Au revoir" : la valeur de l'objet s1 précédent ne change pas car s1 a le type String et le type String est un type immuable, au lieu de cela, le compilateur crée un nouvel objet String avec " Bye " valeur et s1 qui y est référé. ici, lorsque nous imprimons la valeur s2 , le résultat sera "Hi". pas "Au revoir" parce que s2 faisait référence au précédent objet s1 qui avait " Hi " valeur.

Immuable signifie qu'une fois l'objet créé, aucun de ses membres ne changera. String est immuable car vous ne pouvez pas changer son contenu. Par exemple:

String s1 = "  abc  ";
String s2 = s1.trim();

Dans le code ci-dessus, la chaîne s1 n'a pas changé, un autre objet ( s2 ) a été créé à l'aide de s1 .

Par immuable, on entend simplement non modifiable ou non modifiable. Une fois que l'objet chaîne est créé, ses données ou son état ne peuvent plus être modifiés.

Prenons l'exemple ci-dessous,

class Testimmutablestring{  
  public static void main(String args[]){  
    String s="Future";  
    s.concat(" World");//concat() method appends the string at the end  
    System.out.println(s);//will print Future because strings are immutable objects  
  }  
 }  

Ayons une idée du diagramme ci-dessous,

 entrer la description de l'image ici

Dans ce diagramme, vous pouvez voir le nouvel objet créé en tant que "Monde futur". Mais ne changez pas "Future". Parce que String est immuable . s , référez-vous toujours à "Future". Si vous devez appeler "Future World",

String s="Future";  
s=s.concat(" World");  
System.out.println(s);//print Future World

Pourquoi les objets chaîne sont-ils immuables en Java?

  

Parce que Java utilise le concept de littéral chaîne. Supposons qu'il y a 5 variables de référence, que tous se réfèrent à un objet "Futur". Si une variable de référence change la valeur de l'objet, elle sera affectée à toutes les variables de référence. C’est la raison pour laquelle les objets chaîne sont immuables en Java.

Une fois instancié, ne peut pas être modifié. Considérons une classe qu'une instance de pourrait être utilisée comme clé pour une table de hachage ou similaire. Découvrez les meilleures pratiques Java.

Objets immuables

Un objet est considéré comme immuable si son état ne peut pas changer après sa construction. Le recours maximal à des objets immuables est largement reconnu comme une stratégie judicieuse pour créer un code simple et fiable.

Les objets immuables sont particulièrement utiles dans les applications simultanées. Comme ils ne peuvent pas changer d'état, ils ne peuvent pas être corrompus par une interférence de threads ni être observés dans un état incohérent.

Les programmeurs sont souvent réticents à employer des objets immuables, car ils s'inquiètent du coût de la création d'un nouvel objet par opposition à la mise à jour d'un objet sur place. L'impact de la création d'objets est souvent surestimé et peut être compensé par certaines des efficacités associées aux objets immuables. Celles-ci incluent une réduction des frais généraux due au ramassage des ordures et l'élimination du code nécessaire pour protéger les objets mutables de la corruption.

Les sous-sections suivantes prennent une classe dont les instances sont mutables et dérivent une classe avec des instances immuables. Ce faisant, ils donnent des règles générales pour ce type de conversion et démontrent certains des avantages des objets immuables.

Source

Oracle Docs dit

  

Un objet est considéré comme immuable si son état ne peut pas changer par la suite.   est construit. La dépendance maximale à des objets immuables est largement   acceptée comme une bonne stratégie pour créer un code simple et fiable.

     

Les objets immuables sont particulièrement utiles dans les applications simultanées.   Comme ils ne peuvent pas changer d'état, ils ne peuvent pas être corrompus par un thread   interférence ou observée dans un état incohérent.

J'aime cette phrase du post

.
  

Les objets immuables facilitent la programmation d'accès simultané

Un objet immuable est celui que vous ne pouvez pas modifier après l'avoir créé. Un exemple typique sont les littéraux de chaîne.

Un langage de programmation D, qui devient de plus en plus populaire, a la notion d ’" immuabilité ". par " invariant " mot-clé. Consultez cet article de Dr.Dobb à ce sujet - http: // dobbscodet .com / index.php? option = com_myblog & amp = show = Invariant-Strings.html & Itemid = 29 . Cela explique parfaitement le problème.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top